saltfish 0.2.39 → 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 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
+ [![npm version](https://img.shields.io/npm/v/saltfish.svg)](https://www.npmjs.com/package/saltfish)
6
+ [![License](https://img.shields.io/npm/l/saltfish.svg)](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)