saltfish 0.2.41 → 0.2.45
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,409 +1,646 @@
|
|
|
1
1
|
# Saltfish
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Interactive video-guided tours for web applications. Create engaging onboarding experiences with synchronized video playback, interactive overlays, and smart triggering.
|
|
4
4
|
|
|
5
5
|
[](https://www.npmjs.com/package/saltfish)
|
|
6
6
|
[](https://github.com/Saltfish-AB/playlist-player/blob/main/LICENSE)
|
|
7
7
|
|
|
8
8
|
## Features
|
|
9
9
|
|
|
10
|
-
- 🎥 **Video-Guided Tours** -
|
|
11
|
-
- 🎯 **
|
|
12
|
-
- 📱 **Responsive
|
|
13
|
-
- 🎨 **
|
|
14
|
-
- 📊 **Built-in
|
|
10
|
+
- 🎥 **Video-Guided Tours** - Synchronized video content with interactive UI overlays
|
|
11
|
+
- 🎯 **Smart Triggers** - Automatic playlist triggering based on URL, user behavior, and custom conditions
|
|
12
|
+
- 📱 **Responsive** - Automatically adapts to different screen sizes and devices
|
|
13
|
+
- 🎨 **Shadow DOM** - Complete style isolation prevents CSS conflicts
|
|
14
|
+
- 📊 **Analytics** - Built-in tracking for user engagement and completion rates
|
|
15
15
|
- 🔄 **State Persistence** - Resume tours where users left off
|
|
16
|
-
- 🌐 **Multi-language
|
|
16
|
+
- 🌐 **Multi-language** - User-specific language preferences
|
|
17
17
|
- ⚡ **Lightweight** - Minimal bundle size with efficient loading
|
|
18
18
|
|
|
19
19
|
## Installation
|
|
20
20
|
|
|
21
|
+
### CDN (Recommended)
|
|
22
|
+
|
|
23
|
+
```html
|
|
24
|
+
<script src="https://storage.saltfish.ai/player/player.js"></script>
|
|
25
|
+
```
|
|
26
|
+
|
|
21
27
|
### NPM
|
|
22
28
|
|
|
23
29
|
```bash
|
|
24
30
|
npm install saltfish
|
|
25
31
|
```
|
|
26
32
|
|
|
33
|
+
## Quick Start
|
|
34
|
+
|
|
35
|
+
### Basic Setup (CDN)
|
|
36
|
+
|
|
37
|
+
```html
|
|
38
|
+
<!DOCTYPE html>
|
|
39
|
+
<html>
|
|
40
|
+
<head>
|
|
41
|
+
<title>My App</title>
|
|
42
|
+
</head>
|
|
43
|
+
<body>
|
|
44
|
+
<h1>Welcome to My App</h1>
|
|
45
|
+
|
|
46
|
+
<!-- Load Saltfish -->
|
|
47
|
+
<script src="https://storage.saltfish.ai/player/player.js"></script>
|
|
48
|
+
<script>
|
|
49
|
+
// 1. Initialize with your token
|
|
50
|
+
saltfish.init({
|
|
51
|
+
token: 'YOUR_TOKEN_HERE',
|
|
52
|
+
enableAnalytics: true
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
// 2. Identify the user
|
|
56
|
+
saltfish.identify('user-123', {
|
|
57
|
+
email: 'user@example.com',
|
|
58
|
+
name: 'John Doe',
|
|
59
|
+
language: 'en'
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
// 3. (Optional) Manually start a playlist
|
|
63
|
+
saltfish.startPlaylist('playlist-id');
|
|
64
|
+
</script>
|
|
65
|
+
</body>
|
|
66
|
+
</html>
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
### NPM/ES Modules
|
|
70
|
+
|
|
27
71
|
```javascript
|
|
28
|
-
import
|
|
72
|
+
import saltfish from 'saltfish';
|
|
29
73
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
74
|
+
// Initialize
|
|
75
|
+
saltfish.init({
|
|
76
|
+
token: 'YOUR_TOKEN_HERE',
|
|
77
|
+
enableAnalytics: true
|
|
34
78
|
});
|
|
35
79
|
|
|
36
|
-
|
|
80
|
+
// Identify user
|
|
81
|
+
saltfish.identify('user-123', {
|
|
82
|
+
email: 'user@example.com',
|
|
83
|
+
language: 'en'
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
// Start playlist
|
|
87
|
+
saltfish.startPlaylist('playlist-id');
|
|
37
88
|
```
|
|
38
89
|
|
|
39
|
-
|
|
90
|
+
## API Reference
|
|
40
91
|
|
|
41
|
-
|
|
42
|
-
<!-- Include the script -->
|
|
43
|
-
<script src="https://storage.saltfish.ai/player/player.js"></script>
|
|
92
|
+
### `saltfish.init(config)`
|
|
44
93
|
|
|
45
|
-
|
|
46
|
-
<div id="player-container"></div>
|
|
94
|
+
Initialize the Saltfish player. Must be called before any other methods.
|
|
47
95
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
96
|
+
**Parameters:**
|
|
97
|
+
- `config` (Object or String):
|
|
98
|
+
- If string: Your API token
|
|
99
|
+
- If object:
|
|
100
|
+
- `token` (String, required): Your API token from Saltfish dashboard
|
|
101
|
+
- `enableAnalytics` (Boolean, optional): Enable/disable analytics tracking (default: `true`)
|
|
53
102
|
|
|
54
|
-
|
|
55
|
-
|
|
103
|
+
**Returns:** `Promise<void>` (can be called without await)
|
|
104
|
+
|
|
105
|
+
**Examples:**
|
|
106
|
+
|
|
107
|
+
```javascript
|
|
108
|
+
// Simple initialization with token only
|
|
109
|
+
saltfish.init('YOUR_TOKEN_HERE');
|
|
110
|
+
|
|
111
|
+
// With configuration object
|
|
112
|
+
saltfish.init({
|
|
113
|
+
token: 'YOUR_TOKEN_HERE',
|
|
114
|
+
enableAnalytics: false // Disable analytics for development
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
// Using async/await
|
|
118
|
+
await saltfish.init('YOUR_TOKEN_HERE');
|
|
119
|
+
|
|
120
|
+
// Using events
|
|
121
|
+
saltfish.on('initialized', () => {
|
|
122
|
+
console.log('Saltfish ready!');
|
|
123
|
+
});
|
|
124
|
+
saltfish.init('YOUR_TOKEN_HERE');
|
|
56
125
|
```
|
|
57
126
|
|
|
58
|
-
|
|
127
|
+
### `saltfish.identify(userId, attributes)`
|
|
59
128
|
|
|
60
|
-
|
|
129
|
+
Identify the current user. This enables playlist triggers, progress tracking, and personalized experiences.
|
|
130
|
+
|
|
131
|
+
**Parameters:**
|
|
132
|
+
- `userId` (String, required): Unique identifier for the user
|
|
133
|
+
- `attributes` (Object, optional):
|
|
134
|
+
- `email` (String): User's email address
|
|
135
|
+
- `name` (String): User's display name
|
|
136
|
+
- `language` (String): Language preference (`'en'`, `'sv'`, `'es'`, `'fr'`, `'de'`, or `'auto'` for browser detection)
|
|
137
|
+
- Any custom attributes you want to track
|
|
138
|
+
|
|
139
|
+
**Examples:**
|
|
61
140
|
|
|
62
141
|
```javascript
|
|
63
|
-
//
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
},
|
|
71
|
-
onError: (error) => {
|
|
72
|
-
console.error('Player error:', error);
|
|
73
|
-
}
|
|
142
|
+
// Basic identification
|
|
143
|
+
saltfish.identify('user-123');
|
|
144
|
+
|
|
145
|
+
// With email and name
|
|
146
|
+
saltfish.identify('user-123', {
|
|
147
|
+
email: 'user@example.com',
|
|
148
|
+
name: 'John Doe'
|
|
74
149
|
});
|
|
75
150
|
|
|
76
|
-
//
|
|
77
|
-
|
|
151
|
+
// With language preference
|
|
152
|
+
saltfish.identify('user-123', {
|
|
153
|
+
email: 'user@example.com',
|
|
154
|
+
language: 'sv' // Swedish
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
// Auto-detect language from browser
|
|
158
|
+
saltfish.identify('user-123', {
|
|
159
|
+
language: 'auto'
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
// With custom attributes
|
|
163
|
+
saltfish.identify('user-123', {
|
|
164
|
+
email: 'user@example.com',
|
|
165
|
+
plan: 'premium',
|
|
166
|
+
signupDate: '2024-01-15'
|
|
167
|
+
});
|
|
78
168
|
```
|
|
79
169
|
|
|
80
|
-
###
|
|
170
|
+
### `saltfish.identifyAnonymous(attributes)`
|
|
171
|
+
|
|
172
|
+
Identify anonymous users without backend communication. Uses localStorage for progress tracking and trigger evaluation.
|
|
173
|
+
|
|
174
|
+
**Parameters:**
|
|
175
|
+
- `attributes` (Object, optional): Same as `identify()` method
|
|
176
|
+
|
|
177
|
+
**Examples:**
|
|
81
178
|
|
|
82
179
|
```javascript
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
apiBaseUrl: 'https://api.saltfish.ai',
|
|
87
|
-
autoplay: true,
|
|
88
|
-
enableAnalytics: true,
|
|
89
|
-
startMinimized: false,
|
|
90
|
-
onStepChange: (stepIndex) => {
|
|
91
|
-
console.log('Current step:', stepIndex);
|
|
92
|
-
},
|
|
93
|
-
onComplete: () => {
|
|
94
|
-
console.log('Playlist completed!');
|
|
95
|
-
},
|
|
96
|
-
onError: (error) => {
|
|
97
|
-
console.error('Error:', error);
|
|
98
|
-
}
|
|
180
|
+
// Anonymous user with language preference
|
|
181
|
+
saltfish.identifyAnonymous({
|
|
182
|
+
language: 'en'
|
|
99
183
|
});
|
|
100
184
|
|
|
101
|
-
|
|
185
|
+
// Anonymous user with no data
|
|
186
|
+
saltfish.identifyAnonymous();
|
|
187
|
+
|
|
188
|
+
// With custom tracking data
|
|
189
|
+
saltfish.identifyAnonymous({
|
|
190
|
+
language: 'auto',
|
|
191
|
+
theme: 'dark'
|
|
192
|
+
});
|
|
102
193
|
```
|
|
103
194
|
|
|
104
|
-
|
|
195
|
+
### `saltfish.startPlaylist(playlistId, options)`
|
|
105
196
|
|
|
106
|
-
|
|
107
|
-
|--------|------|---------|-------------|
|
|
108
|
-
| `token` | `string` | **required** | Your Saltfish API token |
|
|
109
|
-
| `containerId` | `string` | **required** | DOM element ID where player will be rendered |
|
|
110
|
-
| `apiBaseUrl` | `string` | `'https://api.saltfish.ai'` | API endpoint URL |
|
|
111
|
-
| `autoplay` | `boolean` | `true` | Start playing automatically |
|
|
112
|
-
| `enableAnalytics` | `boolean` | `true` | Enable analytics tracking |
|
|
113
|
-
| `startMinimized` | `boolean` | `false` | Start player in minimized state |
|
|
114
|
-
| `onComplete` | `function` | `undefined` | Callback when playlist completes |
|
|
115
|
-
| `onError` | `function` | `undefined` | Error handler callback |
|
|
116
|
-
| `onStepChange` | `function` | `undefined` | Callback when video step changes |
|
|
197
|
+
Manually start a specific playlist. If a playlist is already running, it will be reset and the new one started.
|
|
117
198
|
|
|
118
|
-
|
|
199
|
+
**Parameters:**
|
|
200
|
+
- `playlistId` (String, required): The playlist ID from your Saltfish dashboard
|
|
201
|
+
- `options` (Object, optional):
|
|
202
|
+
- `startNodeId` (String): Start from a specific step instead of the beginning
|
|
203
|
+
- `position` (String): Player position (`'bottom-left'` or `'bottom-right'`)
|
|
119
204
|
|
|
120
|
-
|
|
205
|
+
**Returns:** `Promise<void>` (can be called without await)
|
|
121
206
|
|
|
122
|
-
|
|
123
|
-
Load and initialize a playlist by ID.
|
|
207
|
+
**Examples:**
|
|
124
208
|
|
|
125
209
|
```javascript
|
|
126
|
-
|
|
210
|
+
// Basic usage
|
|
211
|
+
saltfish.startPlaylist('playlist-123');
|
|
212
|
+
|
|
213
|
+
// Start from a specific step
|
|
214
|
+
saltfish.startPlaylist('playlist-123', {
|
|
215
|
+
startNodeId: 'step-5'
|
|
216
|
+
});
|
|
217
|
+
|
|
218
|
+
// With custom position
|
|
219
|
+
saltfish.startPlaylist('playlist-123', {
|
|
220
|
+
position: 'bottom-right'
|
|
221
|
+
});
|
|
222
|
+
|
|
223
|
+
// Using async/await
|
|
224
|
+
await saltfish.startPlaylist('playlist-123');
|
|
225
|
+
|
|
226
|
+
// Using events
|
|
227
|
+
saltfish.on('playlistStarted', (data) => {
|
|
228
|
+
console.log('Started:', data.playlist.id);
|
|
229
|
+
});
|
|
230
|
+
saltfish.startPlaylist('playlist-123');
|
|
127
231
|
```
|
|
128
232
|
|
|
129
|
-
|
|
130
|
-
|
|
233
|
+
### Event Listeners
|
|
234
|
+
|
|
235
|
+
Listen to player events using `on()` and remove listeners with `off()`.
|
|
236
|
+
|
|
237
|
+
#### `saltfish.on(eventName, handler)`
|
|
238
|
+
|
|
239
|
+
Register an event listener.
|
|
240
|
+
|
|
241
|
+
**Available Events:**
|
|
242
|
+
- `'initialized'` - Player initialized successfully
|
|
243
|
+
- `'playlistStarted'` - Playlist has started playing
|
|
244
|
+
- `'playlistEnded'` - Playlist completed
|
|
245
|
+
- `'playlistDismissed'` - User dismissed/closed the playlist
|
|
246
|
+
- `'stepStarted'` - New step/video started
|
|
247
|
+
- `'stepEnded'` - Step/video completed
|
|
248
|
+
- `'error'` - An error occurred
|
|
249
|
+
|
|
250
|
+
**Examples:**
|
|
131
251
|
|
|
132
252
|
```javascript
|
|
133
|
-
|
|
253
|
+
// Listen for playlist completion
|
|
254
|
+
saltfish.on('playlistEnded', (data) => {
|
|
255
|
+
console.log('Playlist completed:', data.playlist.id);
|
|
256
|
+
console.log('Completion rate:', data.completionRate);
|
|
257
|
+
});
|
|
258
|
+
|
|
259
|
+
// Listen for step changes
|
|
260
|
+
saltfish.on('stepStarted', (data) => {
|
|
261
|
+
console.log('Now on step:', data.step.id);
|
|
262
|
+
});
|
|
263
|
+
|
|
264
|
+
// Listen for errors
|
|
265
|
+
saltfish.on('error', (data) => {
|
|
266
|
+
console.error('Player error:', data.message);
|
|
267
|
+
});
|
|
268
|
+
|
|
269
|
+
// Listen for initialization
|
|
270
|
+
saltfish.on('initialized', () => {
|
|
271
|
+
console.log('Player ready!');
|
|
272
|
+
});
|
|
134
273
|
```
|
|
135
274
|
|
|
136
|
-
#### `
|
|
137
|
-
|
|
275
|
+
#### `saltfish.off(eventName, handler)`
|
|
276
|
+
|
|
277
|
+
Remove an event listener.
|
|
278
|
+
|
|
279
|
+
**Returns:** `boolean` - `true` if listener was removed, `false` if not found
|
|
280
|
+
|
|
281
|
+
**Example:**
|
|
138
282
|
|
|
139
283
|
```javascript
|
|
140
|
-
|
|
284
|
+
function onPlaylistEnd(data) {
|
|
285
|
+
console.log('Playlist ended:', data.playlist.id);
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
// Add listener
|
|
289
|
+
saltfish.on('playlistEnded', onPlaylistEnd);
|
|
290
|
+
|
|
291
|
+
// Remove listener
|
|
292
|
+
saltfish.off('playlistEnded', onPlaylistEnd);
|
|
141
293
|
```
|
|
142
294
|
|
|
143
|
-
|
|
144
|
-
|
|
295
|
+
### Other Methods
|
|
296
|
+
|
|
297
|
+
#### `saltfish.getSessionId()`
|
|
298
|
+
|
|
299
|
+
Get the current session ID.
|
|
300
|
+
|
|
301
|
+
**Returns:** `String`
|
|
145
302
|
|
|
146
303
|
```javascript
|
|
147
|
-
|
|
304
|
+
const sessionId = saltfish.getSessionId();
|
|
305
|
+
console.log('Session ID:', sessionId);
|
|
148
306
|
```
|
|
149
307
|
|
|
150
|
-
#### `
|
|
151
|
-
|
|
308
|
+
#### `saltfish.getRunId()`
|
|
309
|
+
|
|
310
|
+
Get the current run ID (unique per playlist execution).
|
|
311
|
+
|
|
312
|
+
**Returns:** `String | null`
|
|
152
313
|
|
|
153
314
|
```javascript
|
|
154
|
-
|
|
315
|
+
const runId = saltfish.getRunId();
|
|
316
|
+
console.log('Run ID:', runId);
|
|
155
317
|
```
|
|
156
318
|
|
|
157
|
-
#### `
|
|
158
|
-
|
|
319
|
+
#### `saltfish.resetPlaylist()`
|
|
320
|
+
|
|
321
|
+
Reset the current playlist to its initial state.
|
|
159
322
|
|
|
160
323
|
```javascript
|
|
161
|
-
|
|
324
|
+
saltfish.resetPlaylist();
|
|
162
325
|
```
|
|
163
326
|
|
|
164
|
-
#### `
|
|
165
|
-
|
|
327
|
+
#### `saltfish.destroy()`
|
|
328
|
+
|
|
329
|
+
Destroy the player instance and clean up all resources.
|
|
166
330
|
|
|
167
331
|
```javascript
|
|
168
|
-
|
|
169
|
-
console.log(state.currentStepIndex, state.isPlaying);
|
|
332
|
+
saltfish.destroy();
|
|
170
333
|
```
|
|
171
334
|
|
|
172
|
-
|
|
335
|
+
#### `saltfish.version()`
|
|
336
|
+
|
|
337
|
+
Get the current Saltfish player version.
|
|
173
338
|
|
|
174
|
-
|
|
339
|
+
**Returns:** `String`
|
|
175
340
|
|
|
176
341
|
```javascript
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
342
|
+
const version = saltfish.version();
|
|
343
|
+
console.log('Saltfish version:', version);
|
|
344
|
+
```
|
|
180
345
|
|
|
181
|
-
|
|
182
|
-
console.log('Current video ended');
|
|
183
|
-
});
|
|
346
|
+
## Playlist Triggers
|
|
184
347
|
|
|
185
|
-
|
|
186
|
-
console.error('Player error:', error);
|
|
187
|
-
});
|
|
188
|
-
```
|
|
348
|
+
Playlists can be automatically triggered based on conditions you configure in the Saltfish CMS. No code changes needed!
|
|
189
349
|
|
|
190
|
-
|
|
350
|
+
### Trigger Types
|
|
191
351
|
|
|
192
|
-
|
|
352
|
+
#### URL-Based Triggers
|
|
193
353
|
|
|
194
|
-
|
|
195
|
-
- `loading` - Fetching playlist data
|
|
196
|
-
- `playing` - Video is playing
|
|
197
|
-
- `paused` - Video is paused
|
|
198
|
-
- `minimized` - Player minimized to corner
|
|
199
|
-
- `completed` - Playlist finished
|
|
200
|
-
- `error` - Error occurred
|
|
354
|
+
Automatically show playlists when users visit specific URLs:
|
|
201
355
|
|
|
202
|
-
|
|
356
|
+
```javascript
|
|
357
|
+
// Just initialize and identify - triggers happen automatically
|
|
358
|
+
saltfish.init('YOUR_TOKEN');
|
|
359
|
+
saltfish.identify('user-123');
|
|
203
360
|
|
|
204
|
-
|
|
361
|
+
// Playlist will auto-trigger when user navigates to configured URLs
|
|
362
|
+
// e.g., "/dashboard", "/pricing", "/features/*"
|
|
363
|
+
```
|
|
205
364
|
|
|
206
|
-
|
|
365
|
+
**CMS Configuration Examples:**
|
|
366
|
+
- **Exact match**: `/dashboard` - Only triggers on exactly `/dashboard`
|
|
367
|
+
- **Contains**: `/dashboard` with "contains" mode - Triggers on `/dashboard`, `/dashboard/settings`, etc.
|
|
368
|
+
- **Wildcard**: `/products/*` - Triggers on any product page
|
|
369
|
+
- **Regex**: `/playlist-[0-9]+` - Triggers on `/playlist-1`, `/playlist-42`, etc.
|
|
207
370
|
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
371
|
+
#### Element Click Triggers
|
|
372
|
+
|
|
373
|
+
Trigger playlists when users click specific elements:
|
|
374
|
+
|
|
375
|
+
```html
|
|
376
|
+
<button id="help-button">Help</button>
|
|
377
|
+
<button class="support-btn">Support</button>
|
|
378
|
+
<button data-action="guide">Guide</button>
|
|
379
|
+
|
|
380
|
+
<script>
|
|
381
|
+
saltfish.init('YOUR_TOKEN');
|
|
382
|
+
saltfish.identify('user-123');
|
|
383
|
+
|
|
384
|
+
// Configure in CMS with CSS selector:
|
|
385
|
+
// #help-button, .support-btn, [data-action="guide"]
|
|
386
|
+
</script>
|
|
215
387
|
```
|
|
216
388
|
|
|
217
|
-
|
|
389
|
+
#### Conditional Triggers
|
|
390
|
+
|
|
391
|
+
Configure complex trigger logic in the CMS:
|
|
218
392
|
|
|
219
|
-
|
|
393
|
+
- **Once per user**: Show playlist only once, never again
|
|
394
|
+
- **Seen/Not seen**: Trigger based on whether user has seen other playlists
|
|
395
|
+
- **A/B Testing**: Show different playlists to different user segments
|
|
396
|
+
- **Combine conditions**: URL + click + seen/not seen with AND/OR logic
|
|
397
|
+
|
|
398
|
+
### Manual Override
|
|
399
|
+
|
|
400
|
+
You can always manually start a playlist even if triggers are configured:
|
|
220
401
|
|
|
221
402
|
```javascript
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
analytics.track('Saltfish Player State', {
|
|
225
|
-
state: state.playerState,
|
|
226
|
-
step: state.currentStepIndex
|
|
227
|
-
});
|
|
228
|
-
});
|
|
403
|
+
// Manually start any playlist
|
|
404
|
+
saltfish.startPlaylist('onboarding-tour');
|
|
229
405
|
```
|
|
230
406
|
|
|
231
|
-
|
|
407
|
+
## Common Use Cases
|
|
232
408
|
|
|
233
|
-
|
|
409
|
+
### Onboarding Flow
|
|
234
410
|
|
|
235
411
|
```javascript
|
|
236
|
-
//
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
412
|
+
// Initialize on page load
|
|
413
|
+
saltfish.init({
|
|
414
|
+
token: 'YOUR_TOKEN',
|
|
415
|
+
enableAnalytics: true
|
|
416
|
+
});
|
|
417
|
+
|
|
418
|
+
// Identify user after signup
|
|
419
|
+
saltfish.identify(userId, {
|
|
420
|
+
email: userEmail,
|
|
421
|
+
language: 'auto',
|
|
422
|
+
plan: 'free'
|
|
242
423
|
});
|
|
424
|
+
|
|
425
|
+
// Backend triggers will automatically show onboarding
|
|
426
|
+
// when configured in CMS for new users
|
|
243
427
|
```
|
|
244
428
|
|
|
245
|
-
|
|
429
|
+
### Feature Announcement
|
|
246
430
|
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
- Opera 76+
|
|
431
|
+
```javascript
|
|
432
|
+
saltfish.init('YOUR_TOKEN');
|
|
433
|
+
saltfish.identify(userId);
|
|
251
434
|
|
|
252
|
-
|
|
435
|
+
// Configure in CMS to trigger on /new-feature page
|
|
436
|
+
// Playlist auto-plays when user visits
|
|
437
|
+
```
|
|
253
438
|
|
|
254
|
-
|
|
439
|
+
### Help Button
|
|
255
440
|
|
|
256
|
-
```
|
|
257
|
-
|
|
441
|
+
```html
|
|
442
|
+
<button id="help-btn">Help</button>
|
|
258
443
|
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
autoplay: true
|
|
263
|
-
};
|
|
444
|
+
<script>
|
|
445
|
+
saltfish.init('YOUR_TOKEN');
|
|
446
|
+
saltfish.identify(userId);
|
|
264
447
|
|
|
265
|
-
|
|
266
|
-
|
|
448
|
+
// Configure in CMS with element selector: #help-btn
|
|
449
|
+
// Playlist auto-triggers when user clicks
|
|
450
|
+
</script>
|
|
267
451
|
```
|
|
268
452
|
|
|
269
|
-
|
|
453
|
+
### Context-Specific Tours
|
|
270
454
|
|
|
271
|
-
|
|
455
|
+
```javascript
|
|
456
|
+
// Different tours for different pages
|
|
457
|
+
saltfish.init('YOUR_TOKEN');
|
|
458
|
+
saltfish.identify(userId);
|
|
272
459
|
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
460
|
+
// Configure in CMS:
|
|
461
|
+
// - "/dashboard" → dashboard-tour
|
|
462
|
+
// - "/settings" → settings-tour
|
|
463
|
+
// - "/billing" → billing-tour
|
|
276
464
|
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
465
|
+
// Tours trigger automatically based on URL
|
|
466
|
+
```
|
|
467
|
+
|
|
468
|
+
## Framework Integration
|
|
469
|
+
|
|
470
|
+
### React
|
|
471
|
+
|
|
472
|
+
```jsx
|
|
473
|
+
import { useEffect } from 'react';
|
|
474
|
+
import saltfish from 'saltfish';
|
|
280
475
|
|
|
476
|
+
function App() {
|
|
281
477
|
useEffect(() => {
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
console.log('Onboarding completed!');
|
|
288
|
-
}
|
|
289
|
-
});
|
|
478
|
+
// Initialize Saltfish
|
|
479
|
+
saltfish.init({
|
|
480
|
+
token: process.env.REACT_APP_SALTFISH_TOKEN,
|
|
481
|
+
enableAnalytics: true
|
|
482
|
+
});
|
|
290
483
|
|
|
291
|
-
|
|
484
|
+
// Identify user when auth state changes
|
|
485
|
+
if (user) {
|
|
486
|
+
saltfish.identify(user.id, {
|
|
487
|
+
email: user.email,
|
|
488
|
+
name: user.name,
|
|
489
|
+
language: 'auto'
|
|
490
|
+
});
|
|
292
491
|
}
|
|
293
492
|
|
|
493
|
+
// Cleanup on unmount
|
|
294
494
|
return () => {
|
|
295
|
-
|
|
495
|
+
saltfish.destroy();
|
|
296
496
|
};
|
|
297
|
-
}, []);
|
|
497
|
+
}, [user]);
|
|
298
498
|
|
|
299
|
-
return <div
|
|
499
|
+
return <div>Your App</div>;
|
|
300
500
|
}
|
|
301
501
|
```
|
|
302
502
|
|
|
303
|
-
### Vue
|
|
503
|
+
### Vue
|
|
304
504
|
|
|
305
505
|
```vue
|
|
306
506
|
<template>
|
|
307
|
-
<div
|
|
507
|
+
<div>Your App</div>
|
|
308
508
|
</template>
|
|
309
509
|
|
|
310
510
|
<script>
|
|
311
|
-
import
|
|
511
|
+
import saltfish from 'saltfish';
|
|
312
512
|
|
|
313
513
|
export default {
|
|
314
|
-
name: 'OnboardingTour',
|
|
315
514
|
mounted() {
|
|
316
|
-
|
|
515
|
+
saltfish.init({
|
|
317
516
|
token: process.env.VUE_APP_SALTFISH_TOKEN,
|
|
318
|
-
|
|
319
|
-
onComplete: () => {
|
|
320
|
-
console.log('Tour completed!');
|
|
321
|
-
}
|
|
517
|
+
enableAnalytics: true
|
|
322
518
|
});
|
|
323
519
|
|
|
324
|
-
this.
|
|
520
|
+
if (this.user) {
|
|
521
|
+
saltfish.identify(this.user.id, {
|
|
522
|
+
email: this.user.email,
|
|
523
|
+
language: 'auto'
|
|
524
|
+
});
|
|
525
|
+
}
|
|
325
526
|
},
|
|
326
527
|
beforeUnmount() {
|
|
327
|
-
|
|
528
|
+
saltfish.destroy();
|
|
328
529
|
}
|
|
329
530
|
};
|
|
330
531
|
</script>
|
|
331
532
|
```
|
|
332
533
|
|
|
333
|
-
### Angular
|
|
534
|
+
### Angular
|
|
334
535
|
|
|
335
536
|
```typescript
|
|
336
537
|
import { Component, OnInit, OnDestroy } from '@angular/core';
|
|
337
|
-
import
|
|
538
|
+
import saltfish from 'saltfish';
|
|
338
539
|
|
|
339
540
|
@Component({
|
|
340
|
-
selector: 'app-
|
|
341
|
-
template: '<div
|
|
541
|
+
selector: 'app-root',
|
|
542
|
+
template: '<div>Your App</div>'
|
|
342
543
|
})
|
|
343
|
-
export class
|
|
344
|
-
private player: SaltfishPlayer;
|
|
345
|
-
|
|
544
|
+
export class AppComponent implements OnInit, OnDestroy {
|
|
346
545
|
ngOnInit() {
|
|
347
|
-
|
|
546
|
+
saltfish.init({
|
|
348
547
|
token: environment.saltfishToken,
|
|
349
|
-
|
|
350
|
-
onComplete: () => {
|
|
351
|
-
console.log('Onboarding completed!');
|
|
352
|
-
}
|
|
548
|
+
enableAnalytics: true
|
|
353
549
|
});
|
|
354
550
|
|
|
355
|
-
this.
|
|
551
|
+
if (this.authService.user) {
|
|
552
|
+
saltfish.identify(this.authService.user.id, {
|
|
553
|
+
email: this.authService.user.email,
|
|
554
|
+
language: 'auto'
|
|
555
|
+
});
|
|
556
|
+
}
|
|
356
557
|
}
|
|
357
558
|
|
|
358
559
|
ngOnDestroy() {
|
|
359
|
-
|
|
560
|
+
saltfish.destroy();
|
|
360
561
|
}
|
|
361
562
|
}
|
|
362
563
|
```
|
|
363
564
|
|
|
364
|
-
|
|
565
|
+
### Next.js
|
|
365
566
|
|
|
366
|
-
|
|
567
|
+
```javascript
|
|
568
|
+
// app/layout.js or pages/_app.js
|
|
569
|
+
'use client';
|
|
367
570
|
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
- Ensure the playlist ID is correct
|
|
371
|
-
- Check browser console for errors
|
|
571
|
+
import { useEffect } from 'react';
|
|
572
|
+
import saltfish from 'saltfish';
|
|
372
573
|
|
|
373
|
-
|
|
574
|
+
export default function RootLayout({ children }) {
|
|
575
|
+
useEffect(() => {
|
|
576
|
+
if (typeof window !== 'undefined') {
|
|
577
|
+
saltfish.init({
|
|
578
|
+
token: process.env.NEXT_PUBLIC_SALTFISH_TOKEN,
|
|
579
|
+
enableAnalytics: true
|
|
580
|
+
});
|
|
374
581
|
|
|
375
|
-
|
|
582
|
+
// Identify user if logged in
|
|
583
|
+
// saltfish.identify(userId, { email, language: 'auto' });
|
|
584
|
+
}
|
|
376
585
|
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
});
|
|
586
|
+
return () => {
|
|
587
|
+
if (typeof window !== 'undefined') {
|
|
588
|
+
saltfish.destroy();
|
|
589
|
+
}
|
|
590
|
+
};
|
|
591
|
+
}, []);
|
|
592
|
+
|
|
593
|
+
return <html><body>{children}</body></html>;
|
|
594
|
+
}
|
|
383
595
|
```
|
|
384
596
|
|
|
385
|
-
|
|
597
|
+
## TypeScript Support
|
|
598
|
+
|
|
599
|
+
Full TypeScript definitions included:
|
|
386
600
|
|
|
387
|
-
|
|
601
|
+
```typescript
|
|
602
|
+
import saltfish, { SaltfishAPI } from 'saltfish';
|
|
388
603
|
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
}
|
|
604
|
+
// All methods are fully typed
|
|
605
|
+
saltfish.init({
|
|
606
|
+
token: 'YOUR_TOKEN',
|
|
607
|
+
enableAnalytics: true
|
|
608
|
+
});
|
|
609
|
+
|
|
610
|
+
saltfish.identify('user-123', {
|
|
611
|
+
email: 'user@example.com',
|
|
612
|
+
language: 'en'
|
|
613
|
+
});
|
|
614
|
+
|
|
615
|
+
saltfish.startPlaylist('playlist-id', {
|
|
616
|
+
startNodeId: 'step-2'
|
|
617
|
+
});
|
|
618
|
+
|
|
619
|
+
// Event handlers are typed
|
|
620
|
+
saltfish.on('playlistEnded', (data) => {
|
|
621
|
+
console.log(data.playlist.id); // Fully typed
|
|
622
|
+
console.log(data.completionRate);
|
|
623
|
+
});
|
|
394
624
|
```
|
|
395
625
|
|
|
396
|
-
##
|
|
626
|
+
## Browser Support
|
|
627
|
+
|
|
628
|
+
- Chrome/Edge 90+
|
|
629
|
+
- Firefox 88+
|
|
630
|
+
- Safari 14+
|
|
631
|
+
- Opera 76+
|
|
397
632
|
|
|
398
|
-
|
|
633
|
+
## Getting Started
|
|
399
634
|
|
|
400
|
-
1.
|
|
401
|
-
2.
|
|
402
|
-
3. Create playlists
|
|
635
|
+
1. **Sign up** at [saltfish.ai](https://saltfish.ai)
|
|
636
|
+
2. **Get your token** from the dashboard
|
|
637
|
+
3. **Create playlists** using the CMS
|
|
638
|
+
4. **Configure triggers** (optional) for automatic playback
|
|
639
|
+
5. **Install** Saltfish in your app
|
|
403
640
|
|
|
404
641
|
## Support
|
|
405
642
|
|
|
406
|
-
- 📧 Email: support@saltfish.ai
|
|
643
|
+
- 📧 Email: [support@saltfish.ai](mailto:support@saltfish.ai)
|
|
407
644
|
- 🐛 Issues: [GitHub Issues](https://github.com/Saltfish-AB/playlist-player/issues)
|
|
408
645
|
- 📚 Documentation: [docs.saltfish.ai](https://docs.saltfish.ai)
|
|
409
646
|
|
|
@@ -411,10 +648,6 @@ To use Saltfish, you need an API token. Visit [saltfish.ai](https://saltfish.ai)
|
|
|
411
648
|
|
|
412
649
|
PROPRIETARY - See [LICENSE](./LICENSE) file for details.
|
|
413
650
|
|
|
414
|
-
## Contributing
|
|
415
|
-
|
|
416
|
-
We welcome contributions! Please see our [contributing guidelines](https://github.com/Saltfish-AB/playlist-player/blob/main/CONTRIBUTING.md) for details.
|
|
417
|
-
|
|
418
651
|
---
|
|
419
652
|
|
|
420
653
|
Made with ❤️ by [Saltfish AB](https://saltfish.ai)
|