saltfish 0.2.38 → 0.2.40
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 +420 -0
- package/dist/player.js +2 -2
- package/dist/player.min.js +2 -2
- package/dist/saltfish-playlist-player.es.js +1 -1
- package/dist/saltfish-playlist-player.umd.js +1 -1
- package/package.json +1 -1
package/README.md
ADDED
|
@@ -0,0 +1,420 @@
|
|
|
1
|
+
# Saltfish
|
|
2
|
+
|
|
3
|
+
An interactive video-guided tour system for web applications. Create engaging onboarding experiences and product walkthroughs with synchronized video playback and interactive overlays.
|
|
4
|
+
|
|
5
|
+
[](https://www.npmjs.com/package/saltfish)
|
|
6
|
+
[](https://github.com/Saltfish-AB/playlist-player/blob/main/LICENSE)
|
|
7
|
+
|
|
8
|
+
## Features
|
|
9
|
+
|
|
10
|
+
- 🎥 **Video-Guided Tours** - Synchronize video content with interactive UI elements
|
|
11
|
+
- 🎯 **Interactive Overlays** - Highlight and interact with elements during video playback
|
|
12
|
+
- 📱 **Responsive Design** - Automatically adapts to different screen sizes
|
|
13
|
+
- 🎨 **Customizable UI** - Shadow DOM encapsulation prevents style conflicts
|
|
14
|
+
- 📊 **Built-in Analytics** - Track user engagement and progress
|
|
15
|
+
- 🔄 **State Persistence** - Resume tours where users left off
|
|
16
|
+
- 🌐 **Multi-language Support** - Built-in i18n capabilities
|
|
17
|
+
- ⚡ **Lightweight** - Minimal bundle size with efficient loading
|
|
18
|
+
|
|
19
|
+
## Installation
|
|
20
|
+
|
|
21
|
+
### NPM
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
npm install saltfish
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
```javascript
|
|
28
|
+
import { SaltfishPlayer } from 'saltfish';
|
|
29
|
+
|
|
30
|
+
const player = new SaltfishPlayer({
|
|
31
|
+
token: 'your-token-here',
|
|
32
|
+
containerId: 'player-container',
|
|
33
|
+
apiBaseUrl: 'https://api.saltfish.ai' // optional
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
player.loadPlaylist('playlist-id');
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
### CDN
|
|
40
|
+
|
|
41
|
+
```html
|
|
42
|
+
<!-- Include the script -->
|
|
43
|
+
<script src="https://storage.saltfish.ai/player/player.js"></script>
|
|
44
|
+
|
|
45
|
+
<!-- Create a container -->
|
|
46
|
+
<div id="player-container"></div>
|
|
47
|
+
|
|
48
|
+
<script>
|
|
49
|
+
const player = new SaltfishPlayer({
|
|
50
|
+
token: 'your-token-here',
|
|
51
|
+
containerId: 'player-container'
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
player.loadPlaylist('playlist-id');
|
|
55
|
+
</script>
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
## Quick Start
|
|
59
|
+
|
|
60
|
+
### Basic Usage
|
|
61
|
+
|
|
62
|
+
```javascript
|
|
63
|
+
// Initialize the player
|
|
64
|
+
const player = new SaltfishPlayer({
|
|
65
|
+
token: 'your-api-token',
|
|
66
|
+
containerId: 'player-container',
|
|
67
|
+
// Optional configuration
|
|
68
|
+
onComplete: () => {
|
|
69
|
+
console.log('Tour completed!');
|
|
70
|
+
},
|
|
71
|
+
onError: (error) => {
|
|
72
|
+
console.error('Player error:', error);
|
|
73
|
+
}
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
// Load and play a playlist
|
|
77
|
+
await player.loadPlaylist('playlist-id');
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
### With Custom Configuration
|
|
81
|
+
|
|
82
|
+
```javascript
|
|
83
|
+
const player = new SaltfishPlayer({
|
|
84
|
+
token: 'your-api-token',
|
|
85
|
+
containerId: 'player-container',
|
|
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
|
+
}
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
await player.loadPlaylist('playlist-id');
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
## Configuration Options
|
|
105
|
+
|
|
106
|
+
| Option | Type | Default | Description |
|
|
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 |
|
|
117
|
+
|
|
118
|
+
## API Reference
|
|
119
|
+
|
|
120
|
+
### Methods
|
|
121
|
+
|
|
122
|
+
#### `loadPlaylist(playlistId: string): Promise<void>`
|
|
123
|
+
Load and initialize a playlist by ID.
|
|
124
|
+
|
|
125
|
+
```javascript
|
|
126
|
+
await player.loadPlaylist('playlist-123');
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
#### `play(): void`
|
|
130
|
+
Start or resume video playback.
|
|
131
|
+
|
|
132
|
+
```javascript
|
|
133
|
+
player.play();
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
#### `pause(): void`
|
|
137
|
+
Pause video playback.
|
|
138
|
+
|
|
139
|
+
```javascript
|
|
140
|
+
player.pause();
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
#### `minimize(): void`
|
|
144
|
+
Minimize the player to corner of screen.
|
|
145
|
+
|
|
146
|
+
```javascript
|
|
147
|
+
player.minimize();
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
#### `maximize(): void`
|
|
151
|
+
Restore player to full size.
|
|
152
|
+
|
|
153
|
+
```javascript
|
|
154
|
+
player.maximize();
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
#### `destroy(): void`
|
|
158
|
+
Clean up and remove the player instance.
|
|
159
|
+
|
|
160
|
+
```javascript
|
|
161
|
+
player.destroy();
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
#### `getState(): PlayerState`
|
|
165
|
+
Get current player state.
|
|
166
|
+
|
|
167
|
+
```javascript
|
|
168
|
+
const state = player.getState();
|
|
169
|
+
console.log(state.currentStepIndex, state.isPlaying);
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
### Events
|
|
173
|
+
|
|
174
|
+
Listen to player events using the event system:
|
|
175
|
+
|
|
176
|
+
```javascript
|
|
177
|
+
player.on('stateChange', (newState) => {
|
|
178
|
+
console.log('Player state changed:', newState);
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
player.on('videoEnded', () => {
|
|
182
|
+
console.log('Current video ended');
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
player.on('error', (error) => {
|
|
186
|
+
console.error('Player error:', error);
|
|
187
|
+
});
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
## Player States
|
|
191
|
+
|
|
192
|
+
The player implements a finite state machine with these states:
|
|
193
|
+
|
|
194
|
+
- `idle` - Initial state
|
|
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
|
|
201
|
+
|
|
202
|
+
## Advanced Usage
|
|
203
|
+
|
|
204
|
+
### Custom Styling
|
|
205
|
+
|
|
206
|
+
The player uses Shadow DOM for style encapsulation. To customize appearance, use CSS custom properties:
|
|
207
|
+
|
|
208
|
+
```css
|
|
209
|
+
#player-container {
|
|
210
|
+
--saltfish-primary-color: #0066ff;
|
|
211
|
+
--saltfish-background: #ffffff;
|
|
212
|
+
--saltfish-text-color: #333333;
|
|
213
|
+
--saltfish-border-radius: 8px;
|
|
214
|
+
}
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
### Analytics Integration
|
|
218
|
+
|
|
219
|
+
Track custom events alongside built-in analytics:
|
|
220
|
+
|
|
221
|
+
```javascript
|
|
222
|
+
player.on('stateChange', (state) => {
|
|
223
|
+
// Send to your analytics service
|
|
224
|
+
analytics.track('Saltfish Player State', {
|
|
225
|
+
state: state.playerState,
|
|
226
|
+
step: state.currentStepIndex
|
|
227
|
+
});
|
|
228
|
+
});
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
### Session Management
|
|
232
|
+
|
|
233
|
+
The player automatically manages user sessions with 30-minute persistence:
|
|
234
|
+
|
|
235
|
+
```javascript
|
|
236
|
+
// Sessions are tracked automatically
|
|
237
|
+
// Resume functionality works out of the box
|
|
238
|
+
const player = new SaltfishPlayer({
|
|
239
|
+
token: 'your-token',
|
|
240
|
+
containerId: 'player-container'
|
|
241
|
+
// User progress is automatically saved and restored
|
|
242
|
+
});
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
## Browser Support
|
|
246
|
+
|
|
247
|
+
- Chrome/Edge 90+
|
|
248
|
+
- Firefox 88+
|
|
249
|
+
- Safari 14+
|
|
250
|
+
- Opera 76+
|
|
251
|
+
|
|
252
|
+
## TypeScript Support
|
|
253
|
+
|
|
254
|
+
Full TypeScript definitions are included:
|
|
255
|
+
|
|
256
|
+
```typescript
|
|
257
|
+
import { SaltfishPlayer, PlayerConfig, PlayerState } from 'saltfish';
|
|
258
|
+
|
|
259
|
+
const config: PlayerConfig = {
|
|
260
|
+
token: 'your-token',
|
|
261
|
+
containerId: 'player-container',
|
|
262
|
+
autoplay: true
|
|
263
|
+
};
|
|
264
|
+
|
|
265
|
+
const player = new SaltfishPlayer(config);
|
|
266
|
+
const state: PlayerState = player.getState();
|
|
267
|
+
```
|
|
268
|
+
|
|
269
|
+
## Examples
|
|
270
|
+
|
|
271
|
+
### React Integration
|
|
272
|
+
|
|
273
|
+
```jsx
|
|
274
|
+
import { useEffect, useRef } from 'react';
|
|
275
|
+
import { SaltfishPlayer } from 'saltfish';
|
|
276
|
+
|
|
277
|
+
function OnboardingTour() {
|
|
278
|
+
const containerRef = useRef(null);
|
|
279
|
+
const playerRef = useRef(null);
|
|
280
|
+
|
|
281
|
+
useEffect(() => {
|
|
282
|
+
if (containerRef.current) {
|
|
283
|
+
playerRef.current = new SaltfishPlayer({
|
|
284
|
+
token: process.env.REACT_APP_SALTFISH_TOKEN,
|
|
285
|
+
containerId: 'saltfish-container',
|
|
286
|
+
onComplete: () => {
|
|
287
|
+
console.log('Onboarding completed!');
|
|
288
|
+
}
|
|
289
|
+
});
|
|
290
|
+
|
|
291
|
+
playerRef.current.loadPlaylist('onboarding-tour');
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
return () => {
|
|
295
|
+
playerRef.current?.destroy();
|
|
296
|
+
};
|
|
297
|
+
}, []);
|
|
298
|
+
|
|
299
|
+
return <div id="saltfish-container" ref={containerRef} />;
|
|
300
|
+
}
|
|
301
|
+
```
|
|
302
|
+
|
|
303
|
+
### Vue Integration
|
|
304
|
+
|
|
305
|
+
```vue
|
|
306
|
+
<template>
|
|
307
|
+
<div id="saltfish-container" ref="container"></div>
|
|
308
|
+
</template>
|
|
309
|
+
|
|
310
|
+
<script>
|
|
311
|
+
import { SaltfishPlayer } from 'saltfish';
|
|
312
|
+
|
|
313
|
+
export default {
|
|
314
|
+
name: 'OnboardingTour',
|
|
315
|
+
mounted() {
|
|
316
|
+
this.player = new SaltfishPlayer({
|
|
317
|
+
token: process.env.VUE_APP_SALTFISH_TOKEN,
|
|
318
|
+
containerId: 'saltfish-container',
|
|
319
|
+
onComplete: () => {
|
|
320
|
+
console.log('Tour completed!');
|
|
321
|
+
}
|
|
322
|
+
});
|
|
323
|
+
|
|
324
|
+
this.player.loadPlaylist('onboarding-tour');
|
|
325
|
+
},
|
|
326
|
+
beforeUnmount() {
|
|
327
|
+
this.player?.destroy();
|
|
328
|
+
}
|
|
329
|
+
};
|
|
330
|
+
</script>
|
|
331
|
+
```
|
|
332
|
+
|
|
333
|
+
### Angular Integration
|
|
334
|
+
|
|
335
|
+
```typescript
|
|
336
|
+
import { Component, OnInit, OnDestroy } from '@angular/core';
|
|
337
|
+
import { SaltfishPlayer } from 'saltfish';
|
|
338
|
+
|
|
339
|
+
@Component({
|
|
340
|
+
selector: 'app-onboarding',
|
|
341
|
+
template: '<div id="saltfish-container"></div>'
|
|
342
|
+
})
|
|
343
|
+
export class OnboardingComponent implements OnInit, OnDestroy {
|
|
344
|
+
private player: SaltfishPlayer;
|
|
345
|
+
|
|
346
|
+
ngOnInit() {
|
|
347
|
+
this.player = new SaltfishPlayer({
|
|
348
|
+
token: environment.saltfishToken,
|
|
349
|
+
containerId: 'saltfish-container',
|
|
350
|
+
onComplete: () => {
|
|
351
|
+
console.log('Onboarding completed!');
|
|
352
|
+
}
|
|
353
|
+
});
|
|
354
|
+
|
|
355
|
+
this.player.loadPlaylist('onboarding-tour');
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
ngOnDestroy() {
|
|
359
|
+
this.player?.destroy();
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
```
|
|
363
|
+
|
|
364
|
+
## Troubleshooting
|
|
365
|
+
|
|
366
|
+
### Player not loading
|
|
367
|
+
|
|
368
|
+
- Verify your API token is valid
|
|
369
|
+
- Check that the container element exists in the DOM
|
|
370
|
+
- Ensure the playlist ID is correct
|
|
371
|
+
- Check browser console for errors
|
|
372
|
+
|
|
373
|
+
### Autoplay issues
|
|
374
|
+
|
|
375
|
+
Some browsers block autoplay. The player automatically handles this:
|
|
376
|
+
|
|
377
|
+
```javascript
|
|
378
|
+
const player = new SaltfishPlayer({
|
|
379
|
+
token: 'your-token',
|
|
380
|
+
containerId: 'player-container',
|
|
381
|
+
autoplay: true // Player will show fallback UI if autoplay is blocked
|
|
382
|
+
});
|
|
383
|
+
```
|
|
384
|
+
|
|
385
|
+
### Style conflicts
|
|
386
|
+
|
|
387
|
+
The player uses Shadow DOM to prevent style conflicts. If you need to customize:
|
|
388
|
+
|
|
389
|
+
```css
|
|
390
|
+
/* Use CSS custom properties */
|
|
391
|
+
#player-container {
|
|
392
|
+
--saltfish-z-index: 1000;
|
|
393
|
+
}
|
|
394
|
+
```
|
|
395
|
+
|
|
396
|
+
## Getting a Token
|
|
397
|
+
|
|
398
|
+
To use Saltfish, you need an API token. Visit [saltfish.ai](https://saltfish.ai) to:
|
|
399
|
+
|
|
400
|
+
1. Create an account
|
|
401
|
+
2. Generate an API token
|
|
402
|
+
3. Create playlists and video tours
|
|
403
|
+
|
|
404
|
+
## Support
|
|
405
|
+
|
|
406
|
+
- 📧 Email: support@saltfish.ai
|
|
407
|
+
- 🐛 Issues: [GitHub Issues](https://github.com/Saltfish-AB/playlist-player/issues)
|
|
408
|
+
- 📚 Documentation: [docs.saltfish.ai](https://docs.saltfish.ai)
|
|
409
|
+
|
|
410
|
+
## License
|
|
411
|
+
|
|
412
|
+
PROPRIETARY - See [LICENSE](./LICENSE) file for details.
|
|
413
|
+
|
|
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
|
+
---
|
|
419
|
+
|
|
420
|
+
Made with ❤️ by [Saltfish AB](https://saltfish.ai)
|