saltfish
Version:
An interactive video-guided tour system for web applications
654 lines (485 loc) • 14.4 kB
Markdown
# Saltfish
Interactive video-guided tours for web applications. Create engaging onboarding experiences with synchronized video playback, interactive overlays, and smart triggering.
[](https://www.npmjs.com/package/saltfish)
[](https://github.com/Saltfish-AB/playlist-player/blob/main/LICENSE)
## Features
- 🎥 **Video-Guided Tours** - Synchronized video content with interactive UI overlays
- 🎯 **Smart Triggers** - Automatic playlist triggering based on URL, user behavior, and custom conditions
- 📱 **Responsive** - Automatically adapts to different screen sizes and devices
- 🎨 **Shadow DOM** - Complete style isolation prevents CSS conflicts
- 📊 **Analytics** - Built-in tracking for user engagement and completion rates
- 🔄 **State Persistence** - Resume tours where users left off
- 🌐 **Multi-language** - User-specific language preferences
- ⚡ **Lightweight** - Minimal bundle size with efficient loading
## Installation
### CDN (Recommended)
```html
<script src="https://storage.saltfish.ai/player/player.js"></script>
```
### NPM
```bash
npm install saltfish
```
## Quick Start
### Basic Setup (CDN)
```html
<!DOCTYPE html>
<html>
<head>
<title>My App</title>
</head>
<body>
<h1>Welcome to My App</h1>
<!-- Load Saltfish -->
<script src="https://storage.saltfish.ai/player/player.js"></script>
<script>
// 1. Initialize with your token
saltfish.init({
token: 'YOUR_TOKEN_HERE',
enableAnalytics: true
});
// 2. Identify the user
saltfish.identify('user-123', {
email: 'user@example.com',
name: 'John Doe',
language: 'en'
});
// 3. (Optional) Manually start a playlist
saltfish.startPlaylist('playlist-id');
</script>
</body>
</html>
```
### NPM/ES Modules
```javascript
import saltfish from 'saltfish';
// Initialize
saltfish.init({
token: 'YOUR_TOKEN_HERE',
enableAnalytics: true
});
// Identify user
saltfish.identify('user-123', {
email: 'user@example.com',
language: 'en'
});
// Start playlist
saltfish.startPlaylist('playlist-id');
```
## API Reference
### `saltfish.init(config)`
Initialize the Saltfish player. Must be called before any other methods.
**Parameters:**
- `config` (Object or String):
- If string: Your API token
- If object:
- `token` (String, required): Your API token from Saltfish dashboard
- `enableAnalytics` (Boolean, optional): Enable/disable analytics tracking (default: `true`)
**Returns:** `Promise<void>` (can be called without await)
**Examples:**
```javascript
// Simple initialization with token only
saltfish.init('YOUR_TOKEN_HERE');
// With configuration object
saltfish.init({
token: 'YOUR_TOKEN_HERE',
enableAnalytics: false // Disable analytics for development
});
// Using async/await
await saltfish.init('YOUR_TOKEN_HERE');
// Using events
saltfish.on('initialized', () => {
console.log('Saltfish ready!');
});
saltfish.init('YOUR_TOKEN_HERE');
```
### `saltfish.identify(userId, attributes)`
Identify the current user. This enables playlist triggers, progress tracking, and personalized experiences.
**Parameters:**
- `userId` (String, required): Unique identifier for the user
- `attributes` (Object, optional):
- `email` (String): User's email address
- `name` (String): User's display name
- `language` (String): Language preference (`'en'`, `'sv'`, `'es'`, `'fr'`, `'de'`, or `'auto'` for browser detection)
- Any custom attributes you want to track
**Examples:**
```javascript
// Basic identification
saltfish.identify('user-123');
// With email and name
saltfish.identify('user-123', {
email: 'user@example.com',
name: 'John Doe'
});
// With language preference
saltfish.identify('user-123', {
email: 'user@example.com',
language: 'sv' // Swedish
});
// Auto-detect language from browser
saltfish.identify('user-123', {
language: 'auto'
});
// With custom attributes
saltfish.identify('user-123', {
email: 'user@example.com',
plan: 'premium',
signupDate: '2024-01-15'
});
```
### `saltfish.identifyAnonymous(attributes)`
Identify anonymous users without backend communication. Uses localStorage for progress tracking and trigger evaluation.
**Parameters:**
- `attributes` (Object, optional): Same as `identify()` method
**Examples:**
```javascript
// Anonymous user with language preference
saltfish.identifyAnonymous({
language: 'en'
});
// Anonymous user with no data
saltfish.identifyAnonymous();
// With custom tracking data
saltfish.identifyAnonymous({
language: 'auto',
theme: 'dark'
});
```
### `saltfish.startPlaylist(playlistId, options)`
Manually start a specific playlist. If a playlist is already running, it will be reset and the new one started.
**Parameters:**
- `playlistId` (String, required): The playlist ID from your Saltfish dashboard
- `options` (Object, optional):
- `startNodeId` (String): Start from a specific step instead of the beginning
- `position` (String): Player position (`'bottom-left'` or `'bottom-right'`)
**Returns:** `Promise<void>` (can be called without await)
**Examples:**
```javascript
// Basic usage
saltfish.startPlaylist('playlist-123');
// Start from a specific step
saltfish.startPlaylist('playlist-123', {
startNodeId: 'step-5'
});
// With custom position
saltfish.startPlaylist('playlist-123', {
position: 'bottom-right'
});
// Using async/await
await saltfish.startPlaylist('playlist-123');
// Using events
saltfish.on('playlistStarted', (data) => {
console.log('Started:', data.playlist.id);
});
saltfish.startPlaylist('playlist-123');
```
### Event Listeners
Listen to player events using `on()` and remove listeners with `off()`.
#### `saltfish.on(eventName, handler)`
Register an event listener.
**Available Events:**
- `'initialized'` - Player initialized successfully
- `'playlistStarted'` - Playlist has started playing
- `'playlistEnded'` - Playlist completed
- `'playlistDismissed'` - User dismissed/closed the playlist
- `'stepStarted'` - New step/video started
- `'stepEnded'` - Step/video completed
- `'error'` - An error occurred
**Examples:**
```javascript
// Listen for playlist completion
saltfish.on('playlistEnded', (data) => {
console.log('Playlist completed:', data.playlist.id);
console.log('Completion rate:', data.completionRate);
});
// Listen for step changes
saltfish.on('stepStarted', (data) => {
console.log('Now on step:', data.step.id);
});
// Listen for errors
saltfish.on('error', (data) => {
console.error('Player error:', data.message);
});
// Listen for initialization
saltfish.on('initialized', () => {
console.log('Player ready!');
});
```
#### `saltfish.off(eventName, handler)`
Remove an event listener.
**Returns:** `boolean` - `true` if listener was removed, `false` if not found
**Example:**
```javascript
function onPlaylistEnd(data) {
console.log('Playlist ended:', data.playlist.id);
}
// Add listener
saltfish.on('playlistEnded', onPlaylistEnd);
// Remove listener
saltfish.off('playlistEnded', onPlaylistEnd);
```
### Other Methods
#### `saltfish.getSessionId()`
Get the current session ID.
**Returns:** `String`
```javascript
const sessionId = saltfish.getSessionId();
console.log('Session ID:', sessionId);
```
#### `saltfish.getRunId()`
Get the current run ID (unique per playlist execution).
**Returns:** `String | null`
```javascript
const runId = saltfish.getRunId();
console.log('Run ID:', runId);
```
#### `saltfish.resetPlaylist()`
Reset the current playlist to its initial state.
```javascript
saltfish.resetPlaylist();
```
#### `saltfish.destroy()`
Destroy the player instance and clean up all resources.
```javascript
saltfish.destroy();
```
#### `saltfish.version()`
Get the current Saltfish player version.
**Returns:** `String`
```javascript
const version = saltfish.version();
console.log('Saltfish version:', version);
```
## Playlist Triggers
Playlists can be automatically triggered based on conditions you configure in the Saltfish CMS. No code changes needed!
### Trigger Types
#### URL-Based Triggers
Automatically show playlists when users visit specific URLs:
```javascript
// Just initialize and identify - triggers happen automatically
saltfish.init('YOUR_TOKEN');
saltfish.identify('user-123');
// Playlist will auto-trigger when user navigates to configured URLs
// e.g., "/dashboard", "/pricing", "/features/*"
```
**CMS Configuration Examples:**
- **Exact match**: `/dashboard` - Only triggers on exactly `/dashboard`
- **Contains**: `/dashboard` with "contains" mode - Triggers on `/dashboard`, `/dashboard/settings`, etc.
- **Wildcard**: `/products/*` - Triggers on any product page
- **Regex**: `/playlist-[0-9]+` - Triggers on `/playlist-1`, `/playlist-42`, etc.
#### Element Click Triggers
Trigger playlists when users click specific elements:
```html
<button id="help-button">Help</button>
<button class="support-btn">Support</button>
<button data-action="guide">Guide</button>
<script>
saltfish.init('YOUR_TOKEN');
saltfish.identify('user-123');
// Configure in CMS with CSS selector:
// #help-button, .support-btn, [data-action="guide"]
</script>
```
#### Conditional Triggers
Configure complex trigger logic in the CMS:
- **Once per user**: Show playlist only once, never again
- **Seen/Not seen**: Trigger based on whether user has seen other playlists
- **A/B Testing**: Show different playlists to different user segments
- **Combine conditions**: URL + click + seen/not seen with AND/OR logic
### Manual Override
You can always manually start a playlist even if triggers are configured:
```javascript
// Manually start any playlist
saltfish.startPlaylist('onboarding-tour');
```
## Common Use Cases
### Onboarding Flow
```javascript
// Initialize on page load
saltfish.init({
token: 'YOUR_TOKEN',
enableAnalytics: true
});
// Identify user after signup
saltfish.identify(userId, {
email: userEmail,
language: 'auto',
plan: 'free'
});
// Backend triggers will automatically show onboarding
// when configured in CMS for new users
```
### Feature Announcement
```javascript
saltfish.init('YOUR_TOKEN');
saltfish.identify(userId);
// Configure in CMS to trigger on /new-feature page
// Playlist auto-plays when user visits
```
### Help Button
```html
<button id="help-btn">Help</button>
<script>
saltfish.init('YOUR_TOKEN');
saltfish.identify(userId);
// Configure in CMS with element selector: #help-btn
// Playlist auto-triggers when user clicks
</script>
```
### Context-Specific Tours
```javascript
// Different tours for different pages
saltfish.init('YOUR_TOKEN');
saltfish.identify(userId);
// Configure in CMS:
// - "/dashboard" → dashboard-tour
// - "/settings" → settings-tour
// - "/billing" → billing-tour
// Tours trigger automatically based on URL
```
## Framework Integration
### React
```jsx
import { useEffect } from 'react';
import saltfish from 'saltfish';
function App() {
useEffect(() => {
// Initialize Saltfish
saltfish.init({
token: process.env.REACT_APP_SALTFISH_TOKEN,
enableAnalytics: true
});
// Identify user when auth state changes
if (user) {
saltfish.identify(user.id, {
email: user.email,
name: user.name,
language: 'auto'
});
}
// Cleanup on unmount
return () => {
saltfish.destroy();
};
}, [user]);
return <div>Your App</div>;
}
```
### Vue
```vue
<template>
<div>Your App</div>
</template>
<script>
import saltfish from 'saltfish';
export default {
mounted() {
saltfish.init({
token: process.env.VUE_APP_SALTFISH_TOKEN,
enableAnalytics: true
});
if (this.user) {
saltfish.identify(this.user.id, {
email: this.user.email,
language: 'auto'
});
}
},
beforeUnmount() {
saltfish.destroy();
}
};
</script>
```
### Angular
```typescript
import { Component, OnInit, OnDestroy } from '@angular/core';
import saltfish from 'saltfish';
@Component({
selector: 'app-root',
template: '<div>Your App</div>'
})
export class AppComponent implements OnInit, OnDestroy {
ngOnInit() {
saltfish.init({
token: environment.saltfishToken,
enableAnalytics: true
});
if (this.authService.user) {
saltfish.identify(this.authService.user.id, {
email: this.authService.user.email,
language: 'auto'
});
}
}
ngOnDestroy() {
saltfish.destroy();
}
}
```
### Next.js
```javascript
// app/layout.js or pages/_app.js
'use client';
import { useEffect } from 'react';
import saltfish from 'saltfish';
export default function RootLayout({ children }) {
useEffect(() => {
if (typeof window !== 'undefined') {
saltfish.init({
token: process.env.NEXT_PUBLIC_SALTFISH_TOKEN,
enableAnalytics: true
});
// Identify user if logged in
// saltfish.identify(userId, { email, language: 'auto' });
}
return () => {
if (typeof window !== 'undefined') {
saltfish.destroy();
}
};
}, []);
return <html><body>{children}</body></html>;
}
```
## TypeScript Support
Full TypeScript definitions included:
```typescript
import saltfish, { SaltfishAPI } from 'saltfish';
// All methods are fully typed
saltfish.init({
token: 'YOUR_TOKEN',
enableAnalytics: true
});
saltfish.identify('user-123', {
email: 'user@example.com',
language: 'en'
});
saltfish.startPlaylist('playlist-id', {
startNodeId: 'step-2'
});
// Event handlers are typed
saltfish.on('playlistEnded', (data) => {
console.log(data.playlist.id); // Fully typed
console.log(data.completionRate);
});
```
## Browser Support
- Chrome/Edge 90+
- Firefox 88+
- Safari 14+
- Opera 76+
## Getting Started
1. **Sign up** at [saltfish.ai](https://saltfish.ai)
2. **Get your token** from the dashboard
3. **Create playlists** using the CMS
4. **Configure triggers** (optional) for automatic playback
5. **Install** Saltfish in your app
## Support
- 📧 Email: [support@saltfish.ai](mailto:support@saltfish.ai)
- 🐛 Issues: [GitHub Issues](https://github.com/Saltfish-AB/playlist-player/issues)
- 📚 Documentation: [docs.saltfish.ai](https://docs.saltfish.ai)
## License
PROPRIETARY - See [LICENSE](./LICENSE) file for details.
---
Made with ❤️ by [Saltfish AB](https://saltfish.ai)