UNPKG

saltfish

Version:

An interactive video-guided tour system for web applications

478 lines 13.9 kB
/** * Player state constants */ export declare const PLAYER_STATES: { readonly IDLE: "idle"; readonly LOADING: "loading"; readonly PLAYING: "playing"; readonly PAUSED: "paused"; readonly WAITING_FOR_INTERACTION: "waitingForInteraction"; readonly AUTOPLAY_BLOCKED: "autoplayBlocked"; readonly IDLE_MODE: "idleMode"; readonly MINIMIZED: "minimized"; readonly ERROR: "error"; readonly COMPLETED_WAITING_FOR_INTERACTION: "completedWaitingForInteraction"; readonly COMPLETED: "completed"; readonly CLOSING: "closing"; }; /** * Position constants */ export declare const POSITIONS: { readonly BOTTOM_RIGHT: "bottom-right"; readonly BOTTOM_LEFT: "bottom-left"; }; /** * Storage keys */ export declare const STORAGE_KEYS: { readonly PROGRESS: "saltfish_progress"; readonly SESSION: "saltfish_session"; readonly ANONYMOUS_USER: "saltfish_anonymous_user_data"; readonly PENDING_NAVIGATION: "saltfish_pending_navigation"; }; /** * Event names */ export declare const EVENT_NAMES: { readonly INITIALIZED: "initialized"; readonly PLAYLIST_STARTED: "playlistStarted"; readonly PLAYLIST_ENDED: "playlistEnded"; readonly PLAYLIST_DISMISSED: "playlistDismissed"; readonly STEP_STARTED: "stepStarted"; readonly STEP_ENDED: "stepEnded"; readonly PLAYER_PAUSED: "playerPaused"; readonly PLAYER_RESUMED: "playerResumed"; readonly PLAYER_MINIMIZED: "playerMinimized"; readonly PLAYER_MAXIMIZED: "playerMaximized"; readonly USER_DATA_LOADED: "userDataLoaded"; readonly INTERACTION_REQUIRED: "interactionRequired"; readonly INTERACTION_COMPLETED: "interactionCompleted"; readonly ERROR: "error"; }; /** * API constants */ export declare const API: { readonly BASE_URL: "https://player.saltfish.ai"; readonly SHARE_BASE_URL: "https://studio-api.saltfish.ai/studio/flows2/share"; readonly ENDPOINTS: { readonly VALIDATE_TOKEN: "/validate-token"; readonly USERS: "/clients/{token}/users/{userId}"; }; }; /** * CSS class names */ export declare const CSS_CLASSES: { readonly PLAYER: "sf-player"; readonly PLAYER_MINIMIZED: "sf-player--minimized"; readonly VIDEO_CONTAINER: "sf-video-container"; readonly CONTROLS_CONTAINER: "sf-controls-container"; readonly LOGO: "sf-player__logo"; readonly ERROR: "saltfish-error"; readonly ERROR_DISPLAY: "sf-error-display"; readonly ERROR_DISPLAY_VISIBLE: "sf-error-display--visible"; readonly ERROR_DISPLAY_CONTENT: "sf-error-display__content"; readonly ERROR_DISPLAY_MESSAGE: "sf-error-display__message"; readonly LOADING_SPINNER: "sf-loading-spinner"; }; /** * Default configuration */ export declare const DEFAULTS: { readonly POSITION: "bottom-right"; readonly PERSISTENCE: true; readonly SESSION_RECORDING: false; readonly ANALYTICS_ENABLED: true; }; /** * Represents the state of the Saltfish playlist Player */ export type PlayerState = typeof PLAYER_STATES[keyof typeof PLAYER_STATES]; /** * Available position options */ export type Position = typeof POSITIONS[keyof typeof POSITIONS]; /** * Base interface for managers that can be reset instead of recreated */ export interface ResettableManager { /** Resets the manager to initial state for reuse */ reset(): void; /** Completely destroys the manager and cleans up resources */ destroy(): void; } /** * Configuration options for initializing the Saltfish playlist Player */ export interface SaltfishConfig { /** API token for authentication */ token: string; /** Whether to enable analytics data collection and sending (default: true) */ enableAnalytics?: boolean; /** Whether to show the saltfish logo at the bottom of the widget (set from backend) */ showLogo?: boolean; } /** * Options for starting a playlist */ export interface PlaylistOptions { position?: Position; startNodeId?: string; once?: boolean; persistence?: boolean; _triggeredByTriggerManager?: boolean; _startedFromShareLink?: boolean; _isGlobalShare?: boolean; } /** * User identification data */ export interface UserData { id: string; [key: string]: any; } /** * Point coordinates for cursor animation */ export interface Point { x: number; y: number; time?: number; } /** * Interactive button overlay configuration */ export interface ButtonOverlay { id: string; text: string; style?: any; action: { type: 'next' | 'goto' | 'url' | 'playlist'; target: string; url?: string; }; } /** * Expected element size for validation * Used to validate that found elements match the expected size from recording */ export interface ExpectedSize { width: number; height: number; } /** * Expected element tag and text for validation * Used to validate that found elements match the expected tag name and text content from recording * This is tried first before falling back to size-based validation */ export interface ExpectedElement { tagName: string; textContent: string; } /** * Cursor animation configuration */ export interface CursorAnimation { easing?: 'linear' | 'ease-in' | 'ease-out' | 'ease-in-out'; targetSelector: string; mode?: 'pointer' | 'selection'; /** * Time in seconds from video start when the animation should appear * - Default: 0 (immediate, at video start) * - Negative values: Treated as 0 (immediate) * - Values exceeding video duration: Animation triggers immediately once duration is known * - Invalid values (NaN, Infinity): Treated as 0 (immediate) * - If video ends and step transitions: Animation is cancelled (won't show on next step) */ showAtSeconds?: number; selectionStyles?: { borderColor?: string; borderWidth?: string; borderRadius?: string; padding?: string | number; }; expectedSize?: ExpectedSize; expectedElement?: ExpectedElement; } /** * Step transition condition */ export interface TransitionCondition { type: 'timeout' | 'dom-click' | 'url-path' | 'dom-element-visible'; target?: string; value?: any; timeout?: number; nextStep: string; expectedSize?: ExpectedSize; expectedElement?: ExpectedElement; } /** * Transcript chunk with timing information (word-level) */ export interface TranscriptChunk { text: string; start: number; end: number; } /** * Transcript data for a video step */ export interface Transcript { text: string; chunks: TranscriptChunk[]; } /** * Translation data for a step */ export interface StepTranslation { videoUrl: string; gifUrl: string; audioUrl: string; transcript: Transcript; } /** * URL requirement for step validation */ export interface UrlRequirement { pattern: string; matchType: 'exact' | 'contains' | 'regex'; } /** * Step configuration */ export interface Step { id: string; videoUrl: string; compressedUrl?: string; audioUrl?: string; gifUrl?: string; position?: Position; buttons?: ButtonOverlay[]; cursorAnimations?: CursorAnimation[]; transitions: TransitionCondition[]; transcript?: Transcript; translations?: Record<string, StepTranslation>; urlRequirement?: UrlRequirement; } /** * Device type options */ export type DeviceType = 'desktop' | 'mobile' | 'both'; /** * Playlist manifest interface */ export interface PlaylistManifest { id: string; name: string; version: string; position: Position; startStep: string; steps: Step[]; translations?: Record<string, Record<string, string>>; cursorColor?: string; cursorLabel?: string; deviceType?: DeviceType; idleMode?: boolean; compactFirstStep?: boolean; compactLabel?: string; isPersistent?: boolean; captions?: boolean; avatarThumbnailUrl?: string; } /** * Error event data with enriched diagnostic information */ export interface ErrorEventData { message: string; stack?: string; errorType: string; videoUrl?: string; mediaErrorCode?: number; mediaErrorMessage?: string; failureReason?: 'timeout' | 'media_error' | 'load_error' | 'unknown'; } /** * Analytics event */ export interface AnalyticsEvent { type: 'playlistStart' | 'playlistComplete' | 'stepStarted' | 'stepComplete' | 'interaction' | 'error' | 'playerPaused' | 'playerResumed' | 'playerMinimized' | 'playerMaximized'; playlistId: string; stepId?: string; runId: string; timestamp: number; data?: any; } /** * Available playlist metadata */ export interface PlaylistMetadata { id: string; name: string; description?: string; thumbnailUrl?: string; } /** * Represents a single user attribute condition for trigger evaluation */ export interface UserAttributeCondition { attributeKey: string; attributeType: 'string' | 'boolean' | 'int' | 'date'; operator: 'equals' | 'notEquals' | 'greaterThan' | 'lessThan'; value: string; } /** * Represents trigger conditions for playlists with automatic triggers */ export interface PlaylistTriggers { maxVisits: number | null; operators: string[]; playlistNotSeen: string[]; playlistSeen: string[]; url: string | null; urlMatchType?: 'exact' | 'contains' | 'regex'; elementClicked: string | null; elementClickedExpectedSize?: ExpectedSize; elementClickedExpectedElement?: ExpectedElement; elementVisible: string | null; elementVisibleExpectedSize?: ExpectedSize; elementVisibleExpectedElement?: ExpectedElement; userAttributes: UserAttributeCondition[]; } /** * Represents a playlist entry with path info returned by the backend */ export interface PlaylistPathInfo { id: string; path: string; hasTriggers: boolean; autoStart?: boolean; triggers?: PlaylistTriggers; deviceType?: DeviceType; } /** * State store */ export interface SaltfishStore { config: SaltfishConfig | null; user: UserData | null; userData: BackendUserData | null; readonly currentState: PlayerState; manifest: PlaylistManifest | null; currentStepId: string | null; isMinimized: boolean; position: { x: number; y: number; } | null; progress: Record<string, Record<string, unknown>>; error: Error | null; playlistOptions: PlaylistOptions | null; backendPlaylists?: PlaylistPathInfo[]; isAdmin?: boolean; isMuted: boolean; abTests?: ABTestConfig[]; abTestAssignments?: Record<string, ABTestAssignment>; initialize: (config: SaltfishConfig) => Promise<void>; identifyUser: (userId: string, userData?: Record<string, unknown>) => void; setUserData: (userData: BackendUserData) => void; setManifest: (manifest: PlaylistManifest, startStepId: string) => void; play: () => void; pause: () => void; minimize: () => void; maximize: () => void; goToStep: (stepId: string) => void; reset: () => void; setError: (error: Error) => void; setAutoplayFallback: () => void; setIdleMode: () => void; setMuted: (muted: boolean) => void; setPlaylistOptions: (options: PlaylistOptions) => void; setBackendPlaylists?: (playlists: PlaylistPathInfo[]) => void; setIsAdmin?: (isAdmin: boolean) => void; completePlaylist: () => void; resetForNewPlaylist: () => void; loadPlaylistProgress: (playlistId: string, progress: Record<string, unknown>) => void; registerStateMachineActions: (actions: Record<string, any>) => void; sendStateMachineEvent: (event: any) => void; updateProgressWithCompletion: (playlistId: string, currentStepId: string) => void; setABTests: (abTests: ABTestConfig[]) => void; setABTestAssignments: (assignments: Record<string, ABTestAssignment>) => void; getFilteredPlaylists: () => PlaylistPathInfo[]; } /** * Interface for watched playlist status */ export interface WatchedPlaylistStatus { status: 'completed' | 'in_progress' | 'dismissed'; timestamp?: number; visitCount?: number; [key: string]: unknown; } /** * Interface for watched playlists collection */ export interface WatchedPlaylists extends Record<string, WatchedPlaylistStatus | undefined> { } /** * A/B Test Configuration */ export interface ABTestConfig { id: string; name: string; playlistId: string; testType?: 'percentage' | 'userList'; percentage?: number; isActive: boolean; } /** * A/B Test Assignment */ export interface ABTestAssignment { testId: string; assigned: boolean; assignedAt: number; } /** * Interface for user data from backend */ export interface BackendUserData { watchedPlaylists: WatchedPlaylists; abTestAssignments?: Record<string, ABTestAssignment>; language?: string; [key: string]: unknown; } /** * Interface for anonymous user data stored in localStorage */ export interface AnonymousUserData { userId: string; userData: Record<string, unknown>; watchedPlaylists: WatchedPlaylists; timestamp: number; abTestAssignments?: Record<string, ABTestAssignment>; } /** * Pending navigation data for cross-page URL transitions * Used when a step has a url-path transition and the user navigates to a non-SPA page * causing a hard refresh. This allows resuming from the correct step on the new page. */ export interface PendingNavigation { playlistId: string; nextStepId: string; urlPattern: string; timestamp: number; } /** * Interface for state machine - flexible structure to avoid circular imports */ export interface StateMachine { send?: (action: string | { type: string; [key: string]: unknown; }) => void; registerActions?: (actions: Record<string, () => void>) => void; [key: string]: unknown; } //# sourceMappingURL=index.d.ts.map