saltfish 0.1.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.
Files changed (73) hide show
  1. package/LICENSE +34 -0
  2. package/README.md +633 -0
  3. package/dist/components/buttons/ExitButton.d.ts +9 -0
  4. package/dist/components/buttons/ExitButton.d.ts.map +1 -0
  5. package/dist/components/buttons/MinimizeButton.d.ts +11 -0
  6. package/dist/components/buttons/MinimizeButton.d.ts.map +1 -0
  7. package/dist/components/buttons/PlayPauseButton.d.ts +13 -0
  8. package/dist/components/buttons/PlayPauseButton.d.ts.map +1 -0
  9. package/dist/core/SaltfishPlayer.d.ts +154 -0
  10. package/dist/core/SaltfishPlayer.d.ts.map +1 -0
  11. package/dist/core/stateMachine.d.ts +119 -0
  12. package/dist/core/stateMachine.d.ts.map +1 -0
  13. package/dist/core/stateMachineConfig.d.ts +7 -0
  14. package/dist/core/stateMachineConfig.d.ts.map +1 -0
  15. package/dist/core/store.d.ts +27 -0
  16. package/dist/core/store.d.ts.map +1 -0
  17. package/dist/index.d.ts +127 -0
  18. package/dist/index.d.ts.map +1 -0
  19. package/dist/managers/AnalyticsManager.d.ts +107 -0
  20. package/dist/managers/AnalyticsManager.d.ts.map +1 -0
  21. package/dist/managers/ButtonManager.d.ts +40 -0
  22. package/dist/managers/ButtonManager.d.ts.map +1 -0
  23. package/dist/managers/CursorManager.d.ts +143 -0
  24. package/dist/managers/CursorManager.d.ts.map +1 -0
  25. package/dist/managers/EventManager.d.ts +127 -0
  26. package/dist/managers/EventManager.d.ts.map +1 -0
  27. package/dist/managers/InteractionManager.d.ts +58 -0
  28. package/dist/managers/InteractionManager.d.ts.map +1 -0
  29. package/dist/managers/PlaylistManager.d.ts +42 -0
  30. package/dist/managers/PlaylistManager.d.ts.map +1 -0
  31. package/dist/managers/SessionManager.d.ts +56 -0
  32. package/dist/managers/SessionManager.d.ts.map +1 -0
  33. package/dist/managers/SessionRecordingManager.d.ts +52 -0
  34. package/dist/managers/SessionRecordingManager.d.ts.map +1 -0
  35. package/dist/managers/ShadowDOMManager.d.ts +35 -0
  36. package/dist/managers/ShadowDOMManager.d.ts.map +1 -0
  37. package/dist/managers/StepTimeoutManager.d.ts +38 -0
  38. package/dist/managers/StepTimeoutManager.d.ts.map +1 -0
  39. package/dist/managers/TransitionManager.d.ts +73 -0
  40. package/dist/managers/TransitionManager.d.ts.map +1 -0
  41. package/dist/managers/VideoManager.d.ts +231 -0
  42. package/dist/managers/VideoManager.d.ts.map +1 -0
  43. package/dist/observers/EventObserver.d.ts +52 -0
  44. package/dist/observers/EventObserver.d.ts.map +1 -0
  45. package/dist/observers/UIObserver.d.ts +43 -0
  46. package/dist/observers/UIObserver.d.ts.map +1 -0
  47. package/dist/patterns/Observer.d.ts +63 -0
  48. package/dist/patterns/Observer.d.ts.map +1 -0
  49. package/dist/patterns/PlayerStateSubject.d.ts +34 -0
  50. package/dist/patterns/PlayerStateSubject.d.ts.map +1 -0
  51. package/dist/player.js +8 -0
  52. package/dist/player.min.js +6 -0
  53. package/dist/saltfish-playlist-player.es.js +7202 -0
  54. package/dist/saltfish-playlist-player.umd.js +1 -0
  55. package/dist/styles/index.d.ts +14 -0
  56. package/dist/styles/index.d.ts.map +1 -0
  57. package/dist/types/index.d.ts +337 -0
  58. package/dist/types/index.d.ts.map +1 -0
  59. package/dist/types.d.ts +5 -0
  60. package/dist/types.d.ts.map +1 -0
  61. package/dist/utils/ErrorHandler.d.ts +106 -0
  62. package/dist/utils/ErrorHandler.d.ts.map +1 -0
  63. package/dist/utils/PositionCalculator.d.ts +90 -0
  64. package/dist/utils/PositionCalculator.d.ts.map +1 -0
  65. package/dist/utils/deviceDetection.d.ts +90 -0
  66. package/dist/utils/deviceDetection.d.ts.map +1 -0
  67. package/dist/utils/dimensions.d.ts +26 -0
  68. package/dist/utils/dimensions.d.ts.map +1 -0
  69. package/dist/utils/logger.d.ts +35 -0
  70. package/dist/utils/logger.d.ts.map +1 -0
  71. package/dist/utils/mockManifest.d.ts +27 -0
  72. package/dist/utils/mockManifest.d.ts.map +1 -0
  73. package/package.json +87 -0
package/LICENSE ADDED
@@ -0,0 +1,34 @@
1
+ SALTFISH PROPRIETARY LICENSE
2
+
3
+ Copyright (c) 2025 Saltfish. All rights reserved.
4
+
5
+ NOTICE: This software and associated documentation files (the "Software") are
6
+ proprietary and confidential information of Saltfish.
7
+
8
+ RESTRICTIONS:
9
+ 1. The Software is licensed, not sold, to authorized customers only.
10
+ 2. Unauthorized copying, distribution, modification, public display, or use is
11
+ strictly prohibited.
12
+ 3. Commercial use is permitted only by authorized licensees with valid commercial
13
+ licenses from Saltfish.
14
+ 4. Reverse engineering, decompilation, or disassembly is prohibited.
15
+ 5. The Software may not be redistributed or sublicensed without explicit written
16
+ permission from Saltfish.
17
+
18
+ PERMITTED USE:
19
+ - Licensed customers may use the Software solely for their internal business
20
+ purposes as specified in their commercial agreement with Saltfish.
21
+ - Evaluation use is permitted for up to 30 days for potential customers.
22
+
23
+ WARRANTY DISCLAIMER:
24
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
25
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
26
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
27
+ SALTFISH BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
28
+ ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
29
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
30
+
31
+ For licensing inquiries, contact: licensing@saltfish.ai
32
+
33
+ This license is effective as of the date of first use and remains in effect
34
+ until terminated by Saltfish or upon breach of these terms.
package/README.md ADDED
@@ -0,0 +1,633 @@
1
+ # Saltfish Player
2
+
3
+ ## Overview
4
+
5
+ Saltfish Player is an interactive, video-guided tour system designed to provide engaging walkthroughs and feature introductions within web applications. It utilizes a core player component that manages video playback, user interactions, state, and transitions between steps defined in a playlist manifest. The player is designed to be embedded via a script tag and controlled through a simple JavaScript API.
6
+
7
+ ## Features
8
+
9
+ * **playlist-Based Guidance:** Delivers interactive tours defined by JSON manifests, outlining steps, videos, and transitions
10
+ * **Video Playback:** Manages loading, playing, pausing, and seeking videos for each step
11
+ * **Interactive Elements:** Supports overlay buttons and interactions with existing DOM elements to guide users
12
+ * **State Management:** Uses Zustand for managing player state (playing, paused, minimized, etc.) and playlist progress
13
+ * **Shadow DOM Encapsulation:** Renders the player UI within a Shadow DOM to prevent style conflicts with the host application
14
+ * **Drag & Minimize:** Allows users to reposition the player via dragging (if enabled) and minimize it
15
+ * **Analytics:** Includes an `AnalyticsManager` for tracking events like playlist start/complete, step views, interactions, and errors.
16
+ * **Transitions:** Manages transitions between steps based on timeouts, DOM clicks, URL path changes, or other interactions
17
+
18
+ ## Technology Stack
19
+
20
+ * **Language:** TypeScript
21
+ * **Bundler:** Vite
22
+ * **State Management:** Zustand
23
+ * **Testing:**
24
+ * Unit/Integration: Jest
25
+ * E2E: Playwright
26
+ * **Build:** Node.js script (`scripts/build.js`)
27
+
28
+ ## Project Structure
29
+
30
+ ```
31
+ .
32
+ ├── e2e/ # End-to-end tests
33
+ ├── scripts/ # Build scripts
34
+ ├── src/
35
+ │ ├── core/ # Core player logic and state management
36
+ │ ├── managers/ # Manages specific functionalities (Video, UI, State, etc.)
37
+ │ ├── styles/ # CSS styles
38
+ │ ├── tests/ # Unit and integration tests
39
+ │ ├── types/ # TypeScript type definitions
40
+ │ ├── utils/ # Utility functions (logger, mocks)
41
+ │ └── index.ts # Main entry point, exposes public API
42
+ ├── .gitignore
43
+ ├── index.html # Development environment HTML
44
+ ├── jest.config.js # Jest configuration
45
+ ├── package.json # Project dependencies and scripts
46
+ ├── playwright.config.ts # Playwright configuration
47
+ ├── tsconfig.json # TypeScript configuration
48
+ ├── vite.config.ts # Vite configuration for main build
49
+ └── vite.test.config.ts # Vite configuration for test server
50
+ ```
51
+
52
+ ## Core Components
53
+
54
+ * **`SaltfishPlayer` (`src/core/SaltfishPlayer.ts`)**: The main orchestrator class. Initializes managers, handles state changes, and manages the player lifecycle.
55
+ * **`saltfishStore` (`src/core/store.ts`)**: Zustand-based store for managing global player state.
56
+ * **`ShadowDOMManager` (`src/managers/ShadowDOMManager.ts`)**: Creates and manages the Shadow DOM container for UI encapsulation.
57
+ * **`VideoManager` (`src/managers/VideoManager.ts`)**: Handles video loading, playback control (play, pause, seek), progress tracking, and completion policies.
58
+ * **`TransitionManager` (`src/managers/TransitionManager.ts`)**: Manages the logic for transitioning between steps based on defined conditions (e.g., DOM clicks, timeouts).
59
+ * **`InteractionManager` (`src/managers/InteractionManager.ts`)**: Handles the creation and events for interactive overlay buttons and DOM interaction listeners defined in steps.
60
+ * **`CursorManager` (`src/managers/CursorManager.ts`)**: Manages the display and animation of a virtual cursor to guide the user.
61
+ * **`ButtonManager` (`src/managers/ButtonManager.ts`)**: Manages the visibility and state of core player controls like play/pause buttons.
62
+ * **`PlaylistManager` (`src/managers/PlaylistManager.ts`)**: Handles playlist-related functionality including tracking playlist progress, updating watched status, and managing playlist state in the backend.
63
+ * **`AnalyticsManager` (`src/managers/AnalyticsManager.ts`)**: Handles queueing and sending analytics events.
64
+
65
+ ## Getting Started
66
+
67
+ ### Prerequisites
68
+
69
+ * Node.js and npm
70
+
71
+ ### Setup
72
+
73
+ 1. Clone the repository.
74
+ 2. Install dependencies:
75
+ ```bash
76
+ npm install
77
+ ```
78
+
79
+ ### Build
80
+
81
+ To create a production build (outputs to `dist/` directory):
82
+ ```bash
83
+ npm run build
84
+ ```
85
+ This script cleans the dist folder, compiles TypeScript, bundles with Vite, and generates CDN-ready player.js and player.min.js files.
86
+
87
+ ### Development
88
+
89
+ #### Main Dev Server
90
+ Runs Vite dev server for index.html.
91
+
92
+ ```bash
93
+ npm run dev
94
+ ```
95
+ Access at http://localhost:3000.
96
+
97
+ #### Test Dev Server
98
+ Runs Vite dev server specifically for the E2E test environment (e2e/test.html).
99
+
100
+ ```bash
101
+ npm run dev:test
102
+ ```
103
+ Access at http://localhost:3010.
104
+
105
+ ## Usage (API)
106
+
107
+ The Saltfish Player is exposed via the global `saltfish` object.
108
+
109
+ ## Installation
110
+
111
+ ### Option 1: npm Package (Recommended)
112
+
113
+ Install the package using npm:
114
+
115
+ ```bash
116
+ npm install saltfish-playlist-player
117
+ ```
118
+
119
+ Then import and use in your application:
120
+
121
+ ```javascript
122
+ // ES modules
123
+ import saltfish from 'saltfish-playlist-player';
124
+
125
+ // CommonJS
126
+ const saltfish = require('saltfish-playlist-player');
127
+
128
+ // Initialize and use
129
+ saltfish.init('YOUR_API_TOKEN');
130
+ saltfish.startPlaylist('your_playlist_id');
131
+ ```
132
+
133
+ ### Option 2: CDN/Script Tag
134
+
135
+ Add the built player.js script to your HTML:
136
+
137
+ ```html
138
+ <!-- Production version (minified) -->
139
+ <script src="https://unpkg.com/saltfish-playlist-player@latest/dist/player.min.js"></script>
140
+
141
+ <!-- Or development version -->
142
+ <script src="https://unpkg.com/saltfish-playlist-player@latest/dist/player.js"></script>
143
+
144
+ <!-- Or from your own hosting -->
145
+ <script src="/path/to/dist/player.js"></script>
146
+ ```
147
+
148
+ ### Include the script
149
+ Add the built player.js script to your HTML.
150
+
151
+ ```html
152
+ <script src="/path/to/dist/player.js"></script>
153
+ ```
154
+
155
+ ### Simplified API (No Await Required)
156
+
157
+ The Saltfish API now features an improved design that eliminates the need to use `await` keywords with its asynchronous methods. The API internally manages the sequencing of operations, making your code cleaner and more straightforward.
158
+
159
+ #### Key Benefits:
160
+ - Call methods directly in sequence without `await`
161
+ - No need for async functions or promise chains
162
+ - Methods still return promises for backward compatibility
163
+ - Built-in command queueing handles proper execution order
164
+ - Error handling is maintained
165
+
166
+ ### Initialize
167
+
168
+ ```javascript
169
+ // Basic initialization with token - no await needed!
170
+ saltfish.init('YOUR_API_TOKEN');
171
+
172
+ // Or with configuration object
173
+ saltfish.init({
174
+ token: 'YOUR_API_TOKEN',
175
+ persistence: true,
176
+ sessionRecording: false,
177
+ enableAnalytics: true // Default: true, set to false to disable analytics in development
178
+ });
179
+
180
+ // Disable analytics for development/testing environments
181
+ saltfish.init({
182
+ token: 'YOUR_API_TOKEN',
183
+ enableAnalytics: false // No analytics events will be sent
184
+ });
185
+ ```
186
+
187
+ #### Configuration Options
188
+
189
+ - **`token`** (string, required): Your Saltfish API token for authentication
190
+ - **`persistence`** (boolean, optional): Whether to persist user progress between sessions (default: `false`)
191
+ - **`sessionRecording`** (boolean, optional): Whether to enable session recording for analytics (default: `false`). When enabled, records one continuous session using sessionId and userId only, independent of playlist runs.
192
+ - **`enableAnalytics`** (boolean, optional): Whether to collect and send analytics data (default: `true`). Set to `false` in development environments to avoid noisy data
193
+
194
+ ### Identify User (Optional)
195
+
196
+ ```javascript
197
+ saltfish.identify('user-123', { email: 'user@example.com', name: 'Test User' });
198
+ ```
199
+
200
+ ### Start a playlist
201
+
202
+ ```javascript
203
+ // No need to use await - call startPlaylist directly after init
204
+ saltfish.init('YOUR_API_TOKEN');
205
+ saltfish.startPlaylist('your_playlist_id');
206
+
207
+ // Or use events to know when initialization is complete
208
+ saltfish.on('initialized', () => {
209
+ console.log('Saltfish player is ready');
210
+ saltfish.startPlaylist('your_playlist_id');
211
+ });
212
+ saltfish.init('YOUR_API_TOKEN');
213
+
214
+ // For those who prefer async/await, this still works too
215
+ async function startPlayer() {
216
+ await saltfish.init('YOUR_API_TOKEN');
217
+ await saltfish.startPlaylist('your_playlist_id');
218
+ }
219
+ ```
220
+
221
+ The player will attempt to fetch the manifest for your_playlist_id from /api/manifests/your_playlist_id. If it fails or if window.demoManifest is set, it will use a mock manifest.
222
+
223
+ #### Playlist Options
224
+
225
+ The `startPlaylist` method accepts an optional second parameter for configuration:
226
+
227
+ ```javascript
228
+ saltfish.startPlaylist('your_playlist_id', {
229
+ position: 'top-left', // Player position: 'top-left', 'top-right', 'bottom-left', 'bottom-right', 'center'
230
+ allowDrag: false, // Whether the player can be dragged (default: true)
231
+ startNodeId: 'step_2', // Start from a specific step instead of the default
232
+ once: true // Only play if user hasn't watched this playlist before
233
+ });
234
+ ```
235
+
236
+ ##### Once Option
237
+
238
+ The `once: true` option ensures a playlist only plays if the user hasn't completed it before. This is useful for onboarding flows or important announcements that should only be shown once:
239
+
240
+ ```javascript
241
+ // User identification is required when using the once option
242
+ saltfish.identify('user-123', { email: 'user@example.com' });
243
+
244
+ // This will only play if the user hasn't completed 'onboarding_tutorial' before
245
+ saltfish.startPlaylist('onboarding_tutorial', { once: true });
246
+ ```
247
+
248
+ **Requirements for `once` option:**
249
+ - User must be identified using `saltfish.identify()` before starting the playlist
250
+ - User data (including watch history) must be loaded from the backend
251
+ - The playlist will be blocked if the user has already completed it
252
+
253
+ **Behavior:**
254
+ - ✅ First time: Playlist plays normally
255
+ - ❌ Subsequent times: Playlist is blocked and an error is thrown
256
+ - 🔄 Without `once` option: Playlist always plays regardless of watch history
257
+
258
+ ### How It Works
259
+
260
+ The Saltfish API implements a command queuing system that:
261
+
262
+ 1. Tracks the initialization state of the player
263
+ 2. Automatically queues commands called before initialization completes
264
+ 3. Processes the queue in the correct order after initialization
265
+ 4. Handles errors appropriately
266
+
267
+ This means you can call methods like `init()` and `startPlaylist()` in sequence without worrying about their asynchronous nature. The API handles the complexity for you.
268
+
269
+ ### Event Subscription
270
+
271
+ As an alternative to promises, you can use the event system to respond to player state changes:
272
+
273
+ ```javascript
274
+ // Subscribe to initialization completion
275
+ saltfish.on('initialized', () => {
276
+ console.log('Player initialized');
277
+ saltfish.startPlaylist('your_playlist_id');
278
+ });
279
+
280
+ // Subscribe to other events
281
+ const playlistEndedListener = event => {
282
+ console.log(`Playlist ${event.playlist.id} ended at ${new Date(event.timestamp)}`);
283
+ // Perform actions when playlist ends
284
+ };
285
+ saltfish.on('playlistEnded', playlistEndedListener);
286
+
287
+ // Unsubscribe when no longer needed
288
+ saltfish.off('playlistEnded', playlistEndedListener);
289
+ ```
290
+
291
+ See [Event Documentation](./docs/events.md) for a complete list of events and payload structures.
292
+
293
+ ### Destroy
294
+
295
+ ```javascript
296
+ saltfish.destroy();
297
+ ```
298
+
299
+ ## Manifest Example
300
+
301
+ A manifest is a JSON object that defines the structure and content of a playlist. It specifies the steps, videos, interactive elements (buttons, DOM interactions, cursor animations), and transitions between steps.
302
+
303
+ Here's an example from the mock data used in the project (src/utils/mockManifest.ts):
304
+
305
+ ```json
306
+ {
307
+ "id": "realistic_mock_playlist",
308
+ "name": "Feature Walkthrough Tour",
309
+ "version": "1.0.0",
310
+ "position": "bottom-right",
311
+ "startStep": "intro",
312
+ "steps": [
313
+ {
314
+ "id": "intro",
315
+ "videoUrl": "https://storage.saltfish.ai/videos/QSWY4Wc8pcBVYHNQM12P.mp4",
316
+ "transitions": [
317
+ {
318
+ "type": "timeout",
319
+ "timeout": 30000,
320
+ "nextStep": "dashboard_overview"
321
+ }
322
+ ]
323
+ },
324
+ {
325
+ "id": "dashboard_overview",
326
+ "videoUrl": "https://storage.saltfish.ai/videos/rEWoNbKLAUQSvLmzHGmv.mp4",
327
+ "cursorAnimations": [
328
+ {
329
+ "duration": 3000,
330
+ "easing": "ease-out",
331
+ "targetSelector": ".container > div:nth-of-type(1)"
332
+ }
333
+ ],
334
+ "domInteractions": [
335
+ {
336
+ "selector": "#feature-chart",
337
+ "action": "hover",
338
+ "waitFor": false
339
+ }
340
+ ],
341
+ "buttons": [
342
+ {
343
+ "id": "skip_button",
344
+ "text": "Skip",
345
+ "position": { "x": 50, "y": 140 },
346
+ "size": { "width": 70, "height": 30 },
347
+ "action": { "type": "goto", "target": "user_settings" }
348
+ }
349
+ ],
350
+ "transitions": [
351
+ {
352
+ "type": "timeout",
353
+ "timeout": 18000,
354
+ "nextStep": "user_settings"
355
+ }
356
+ ]
357
+ },
358
+ {
359
+ "id": "user_settings",
360
+ "videoUrl": "https://storage.saltfish.ai/codeformer/oNcN4ZoQXFFnMmaOwEC3/result.mp4",
361
+ "buttons": [
362
+ {
363
+ "id": "finish_button",
364
+ "text": "Finish Tour",
365
+ "position": { "x": 220, "y": 140 },
366
+ "size": { "width": 120, "height": 40 },
367
+ "action": { "type": "goto", "target": "conclusion" }
368
+ }
369
+ ],
370
+ "transitions": [
371
+ {
372
+ "type": "dom-click",
373
+ "target": ".container > div:nth-of-type(1)",
374
+ "nextStep": "conclusion"
375
+ },
376
+ {
377
+ "type": "url-path",
378
+ "target": "/settings/profile",
379
+ "nextStep": "profile_settings"
380
+ }
381
+ ]
382
+ },
383
+ {
384
+ "id": "profile_settings",
385
+ "videoUrl": "https://storage.saltfish.ai/videos/profile_settings_demo.mp4",
386
+ "transitions": [
387
+ {
388
+ "type": "timeout",
389
+ "timeout": 15000,
390
+ "nextStep": "conclusion"
391
+ }
392
+ ]
393
+ },
394
+ {
395
+ "id": "conclusion",
396
+ "videoUrl": "https://storage.saltfish.ai/memo/5z5IDHYzuGDjx41hpvSo/result.mp4",
397
+ "buttons": [
398
+ {
399
+ "id": "close_button",
400
+ "text": "Close Tour",
401
+ "position": { "x": 220, "y": 140 },
402
+ "size": { "width": 120, "height": 40 },
403
+ "action": { "type": "next", "target": "" }
404
+ }
405
+ ],
406
+ "transitions": []
407
+ }
408
+ ]
409
+ }
410
+ ```
411
+
412
+ ## Testing
413
+
414
+ The project includes unit, integration, and end-to-end tests.
415
+
416
+ ### Run all Jest tests (unit/integration)
417
+ ```bash
418
+ npm test
419
+ ```
420
+
421
+ ### Run Jest tests in watch mode
422
+ ```bash
423
+ npm run test:watch
424
+ ```
425
+
426
+ ### Run Jest tests with coverage
427
+ ```bash
428
+ npm run test:coverage
429
+ ```
430
+
431
+ ### Run Playwright E2E tests
432
+ Ensure the test dev server is not running separately.
433
+ Install browser binaries if needed: `npx playwright install`
434
+ ```bash
435
+ npm run test:e2e
436
+ ```
437
+
438
+ ### Run Playwright E2E tests in UI mode
439
+ ```bash
440
+ npm run test:e2e:ui
441
+ ```
442
+
443
+ See src/tests/README.md for more details on the testing strategy.
444
+
445
+ ## React Dashboard
446
+
447
+ A React-based dashboard has been added to the project for easier testing and demonstration of the Saltfish playlist Player. The dashboard provides a user-friendly interface with multiple routes to trigger and manage different workplaylists.
448
+
449
+ ### Features
450
+
451
+ - Multiple routes with React Router
452
+ - Dashboard for overview statistics
453
+ - Workplaylists page to trigger different interactive guides
454
+ - Settings page to configure the Saltfish player
455
+ - Integration with Saltfish playlist Player
456
+
457
+ ### Running the Dashboard
458
+
459
+ 1. Navigate to the app directory:
460
+ ```bash
461
+ cd app
462
+ ```
463
+
464
+ 2. Install dependencies:
465
+ ```bash
466
+ npm install
467
+ ```
468
+
469
+ 3. Start the development server:
470
+ ```bash
471
+ npm run dev
472
+ ```
473
+
474
+ 4. Open your browser and navigate to:
475
+ ```
476
+ http://localhost:3000
477
+ ```
478
+
479
+ The dashboard will automatically load and integrate with the Saltfish playlist Player. You can use the interface to initialize the player, identify users, and trigger different workplaylists.
480
+
481
+ ### Transition Types
482
+
483
+ The Saltfish player supports multiple types of transitions between steps:
484
+
485
+ #### Timeout Transitions
486
+ Automatic transitions after a specified duration.
487
+
488
+ ```json
489
+ {
490
+ "type": "timeout",
491
+ "timeout": 5000, // milliseconds
492
+ "nextStep": "next_step_id"
493
+ }
494
+ ```
495
+
496
+ #### DOM Click Transitions
497
+ Transitions triggered when the user clicks a specific element in the DOM.
498
+
499
+ ```json
500
+ {
501
+ "type": "dom-click",
502
+ "target": ".some-element-selector",
503
+ "nextStep": "next_step_id"
504
+ }
505
+ ```
506
+
507
+ #### URL Path Transitions
508
+ Transitions triggered when the URL path matches a specified pattern. This is useful for playlists that span multiple pages or routes in a single-page application.
509
+
510
+ ```json
511
+ {
512
+ "type": "url-path",
513
+ "target": "/dashboard", // Will match when URL path is exactly "/dashboard"
514
+ "nextStep": "dashboard_step"
515
+ }
516
+ ```
517
+
518
+ ```json
519
+ {
520
+ "type": "url-path",
521
+ "target": "^/settings/.*", // Regex pattern matching any URL starting with "/settings/"
522
+ "nextStep": "settings_step"
523
+ }
524
+ ```
525
+
526
+ When a URL path transition is specified:
527
+ 1. The player monitors URL changes in the application
528
+ 2. When the URL changes to match the pattern, the player automatically transitions to the next step
529
+ 3. This allows seamless continuation of playlists across different pages or routes
530
+
531
+ ## Build Options
532
+
533
+ The project supports two types of builds:
534
+
535
+ ### Production Build
536
+
537
+ The production build removes all console.log statements to reduce the bundle size. This is the default build type.
538
+
539
+ ```bash
540
+ npm run build
541
+ # or
542
+ npm run build:prod
543
+ ```
544
+
545
+ ### Test Build
546
+
547
+ The test build preserves all console.log statements for easier debugging. This creates a slightly larger bundle but makes troubleshooting much easier.
548
+
549
+ ```bash
550
+ npm run build:test
551
+ ```
552
+
553
+ The test build will generate files with the `-test` suffix to distinguish them from production files.
554
+
555
+ ### Size Comparison
556
+
557
+ - Production build: Optimized for performance and smaller file size
558
+ - Test build: Includes debugging information and console logs
559
+
560
+ ## State Management
561
+
562
+ The player implements a state machine to manage transitions between different states. The main states include:
563
+
564
+ * **idle**: Initial state before the player is used
565
+ * **loading**: Loading a playlist or video
566
+ * **playing**: Video is actively playing
567
+ * **paused**: Video is paused by user action
568
+ * **waitingForInteraction**: Video has ended and waiting for user interaction or transition trigger
569
+ * **autoplayBlocked**: Player requires user interaction for autoplay to continue
570
+ * **minimized**: Player is minimized
571
+ * **error**: An error has occurred
572
+ * **completed**: Playlist has finished playing
573
+
574
+ The `completed` state is triggered when the last video in a playlist finishes playing and there are no transitions defined, or when the `completePlaylist` method is called. This state signals that the player should be destroyed after showing a completion message.
575
+
576
+ ### State Transitions
577
+
578
+ State transitions occur based on various events:
579
+ - User actions (play, pause, minimize)
580
+ - Video completion
581
+ - Transitions based on URL changes, DOM interactions, or timeouts
582
+ - Automatically when a step completes (via `VIDEO_ENDED` event)
583
+
584
+ The player ensures transitions work smoothly by only setting up DOM and URL-based transitions after the video has ended, preventing premature transitions while the video is still playing.
585
+
586
+ ## Progress Bar and Video Playback
587
+
588
+ The player features a sophisticated progress bar system that smoothly updates during video playback:
589
+
590
+ * **Transition Handling**: The progress bar applies different CSS transitions based on the current video state (playing, paused, seeking, ended) to ensure smooth animations without jumps.
591
+
592
+ * **Forced Reflow**: To prevent visual glitches, the player uses forced reflow techniques to ensure CSS changes are applied in the correct sequence.
593
+
594
+ * **Play/Pause Optimization**: When playing or pausing, the progress bar updates immediately to prevent jumpy behavior, especially during rapid state changes.
595
+
596
+ * **Seeking Support**: Special handlers for seeking events ensure the progress bar updates correctly during user-initiated seeking.
597
+
598
+ * **End-of-Video Transition**: A smooth ease-out transition is applied when the video reaches the end, creating a professional finishing effect.
599
+
600
+ These improvements ensure a highly polished user experience with fluid progress tracking during all phases of video playback.
601
+
602
+ ## Transitions Between Steps
603
+
604
+ Transitions control how the player moves from one step to the next in a playlist. The player supports several types of transitions:
605
+
606
+ * **timeout**: Automatically transitions after a specified time
607
+ * **dom-click**: Transitions when a user clicks on a specified DOM element
608
+ * **url-path**: Transitions when the URL path changes to match a specified pattern
609
+ * **dom-element-visible**: Transitions when a specified DOM element becomes visible
610
+ * **interaction**: Transitions based on user interactions with overlay buttons
611
+
612
+ ### Delayed Transition Setup
613
+
614
+ To improve the user experience, DOM-based transitions (dom-click, url-path, dom-element-visible) are only set up after the video has finished playing. This prevents transitions from being triggered prematurely while the user is watching the video.
615
+
616
+ The process works as follows:
617
+
618
+ 1. When a step begins, the video starts playing
619
+ 2. The player waits for the video to complete (`ended` event)
620
+ 3. After the video ends, transitions are set up based on the step's configuration
621
+ 4. The player enters the `waitingForInteraction` state if interaction-based transitions are present
622
+ 5. When a transition is triggered, the player moves to the next step
623
+
624
+ This delayed setup ensures users can watch the entire instructional video before needing to interact with the interface or having URL-based transitions occur.
625
+
626
+ ### Handling Completion
627
+
628
+ When a step has no transitions defined, or when the last step in a playlist is reached, the player automatically transitions to the `completed` state. This can happen in two ways:
629
+
630
+ 1. Auto-completion when no transitions are defined for a step
631
+ 2. Manual completion via the `completePlaylist` method
632
+
633
+ The player then displays a completion message and is automatically destroyed after a short delay.
@@ -0,0 +1,9 @@
1
+ export declare class ExitButton {
2
+ private button;
3
+ private playerElement;
4
+ constructor(playerElement: HTMLElement);
5
+ private handleClick;
6
+ updateVisibility(isMinimized: boolean): void;
7
+ destroy(): void;
8
+ }
9
+ //# sourceMappingURL=ExitButton.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ExitButton.d.ts","sourceRoot":"","sources":["../../../src/components/buttons/ExitButton.ts"],"names":[],"mappings":"AAGA,qBAAa,UAAU;IACrB,OAAO,CAAC,MAAM,CAAoB;IAClC,OAAO,CAAC,aAAa,CAAc;gBAEvB,aAAa,EAAE,WAAW;IAYtC,OAAO,CAAC,WAAW;IAsBZ,gBAAgB,CAAC,WAAW,EAAE,OAAO,GAAG,IAAI;IAS5C,OAAO,IAAI,IAAI;CAIvB"}
@@ -0,0 +1,11 @@
1
+ export declare class MinimizeButton {
2
+ private button;
3
+ private playerElement;
4
+ constructor(playerElement: HTMLElement);
5
+ private handleClick;
6
+ minimize(): void;
7
+ maximize(): void;
8
+ updateVisibility(isMinimized: boolean): void;
9
+ destroy(): void;
10
+ }
11
+ //# sourceMappingURL=MinimizeButton.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"MinimizeButton.d.ts","sourceRoot":"","sources":["../../../src/components/buttons/MinimizeButton.ts"],"names":[],"mappings":"AAEA,qBAAa,cAAc;IACzB,OAAO,CAAC,MAAM,CAAoB;IAClC,OAAO,CAAC,aAAa,CAAc;gBAEvB,aAAa,EAAE,WAAW;IAYtC,OAAO,CAAC,WAAW;IAqBZ,QAAQ,IAAI,IAAI;IAIhB,QAAQ,IAAI,IAAI;IAIhB,gBAAgB,CAAC,WAAW,EAAE,OAAO,GAAG,IAAI;IAS5C,OAAO,IAAI,IAAI;CAIvB"}