/** * Theme-aware color value * Allows setting different colors for light and dark themes */ interface ThemeColor { light: string; dark: string; } /** * Gradient color value * Creates a linear gradient between two colors * @example { from: '#36e3ff', to: '#7c5cff', direction: 'to right' } */ interface GradientColor { from: string; to: string; /** CSS gradient direction (default: 'to right') */ direction?: 'to right' | 'to left' | 'to bottom' | 'to top' | string; } /** * Color value that can be a solid color, gradient, or theme-aware */ type ColorValue = string | GradientColor | ThemeColor | ThemeGradientColor; /** * Theme-aware gradient - different gradients for light/dark themes */ interface ThemeGradientColor { light: string | GradientColor; dark: string | GradientColor; } interface PocketPingConfig { /** Your backend endpoint for self-hosted (e.g., "https://yoursite.com/pocketping") */ endpoint?: string; /** Project ID for SaaS users (e.g., "proj_xxxxxxxxxxxxx") - from dashboard */ projectId?: string; /** Company/operator name displayed in header */ operatorName?: string; /** Operator/company avatar URL (displayed in header) */ operatorAvatar?: string; /** Company logo URL (displayed in header, alternative to avatar) */ logoUrl?: string; /** Header title (defaults to operatorName) */ headerTitle?: string; /** Header subtitle (e.g., "We usually reply within minutes") */ headerSubtitle?: string; /** Welcome message shown when chat opens */ welcomeMessage?: string; /** Placeholder text for message input */ placeholder?: string; /** Color theme */ theme?: 'light' | 'dark' | 'auto'; /** Primary brand color (hex, e.g., "#6366f1") */ primaryColor?: string; /** Text color on primary background (defaults to white) */ primaryTextColor?: string; /** * Header background color or gradient * Can be a solid color, gradient, or theme-aware * @example "#7c5cff" * @example { from: '#36e3ff', to: '#7c5cff' } * @example { light: "#7c5cff", dark: "#202c33" } * @example { light: { from: '#36e3ff', to: '#7c5cff' }, dark: "#202c33" } */ headerColor?: ColorValue; /** * Footer/input area background color * Can be a string (same for both themes) or object with light/dark values * @example "#f0f2f5" or { light: "#f0f2f5", dark: "#202c33" } */ footerColor?: string | ThemeColor; /** * Chat background style: * - 'whatsapp' (default) - WhatsApp-style pattern * - 'dots' - Subtle dot pattern * - 'plain' - Solid color only * - URL string - Custom image URL * Can also be theme-aware with { light: '...', dark: '...' } */ chatBackground?: 'whatsapp' | 'dots' | 'plain' | string | ThemeColor; /** * Toggle button background color or gradient * Can be a solid color, gradient, or theme-aware * @example "#7c5cff" * @example { from: '#36e3ff', to: '#7c5cff' } * @example { light: "#7c5cff", dark: "#7c5cff" } */ toggleColor?: ColorValue; /** Widget position */ position?: 'bottom-right' | 'bottom-left'; /** Distance from edge in pixels (default: 20) */ offset?: number; /** Border radius in pixels (default: 16) */ borderRadius?: number; /** Font family (defaults to system font stack) */ fontFamily?: string; /** Z-index for widget (default: 9999) */ zIndex?: number; /** Toggle button icon: 'chat' | 'message' | 'help' | custom SVG string */ toggleIcon?: 'chat' | 'message' | 'help' | string; /** Custom CSS to inject (for advanced customization) */ customCSS?: string; /** Only show on certain pages (regex patterns) */ showOnPages?: string[]; /** Hide on certain pages (regex patterns) */ hideOnPages?: string[]; /** Delay before showing widget in ms (default: 0) */ showDelay?: number; /** Auto-open chat after delay in ms (0 = disabled) */ autoOpenDelay?: number; /** Auto-open chat when operator sends a message (default: true) */ autoOpenOnMessage?: boolean; /** Play sound on new message */ soundEnabled?: boolean; /** Show unread badge on toggle button */ showUnreadBadge?: boolean; /** Persist chat open/closed state in localStorage */ persistOpenState?: boolean; /** Called when chat window opens */ onOpen?: () => void; /** Called when chat window closes */ onClose?: () => void; /** Called when a message is received */ onMessage?: (message: Message) => void; /** Called when connected to backend */ onConnect?: (sessionId: string) => void; /** Called when connection fails */ onError?: (error: Error) => void; /** Called when a version mismatch is detected */ onVersionWarning?: (warning: VersionWarning) => void; /** Called when a custom event is received from the backend */ onEvent?: (event: CustomEvent) => void; } type MessageStatus = 'sending' | 'sent' | 'delivered' | 'read'; type AttachmentStatus = 'pending' | 'uploading' | 'ready' | 'failed'; interface Attachment { id: string; filename: string; mimeType: string; size: number; url: string; thumbnailUrl?: string; status: AttachmentStatus; } /** Reply reference - either a string ID or embedded data from SSE */ interface ReplyToData { id: string; content: string; sender: string; deleted?: boolean; /** Indicates if the replied message has attachments (e.g., image) */ hasAttachment?: boolean; /** MIME type of the first attachment (for icon display) */ attachmentType?: string; } interface Message { id: string; sessionId: string; content: string; sender: 'visitor' | 'operator' | 'ai'; timestamp: string; /** Reply reference - string ID when sending, object with data from SSE */ replyTo?: string | ReplyToData; metadata?: Record; attachments?: Attachment[]; status?: MessageStatus; deliveredAt?: string; readAt?: string; editedAt?: string; deletedAt?: string; } interface Session { sessionId: string; visitorId: string; operatorOnline: boolean; /** * Whether the team is reachable via a connected bridge (Telegram/Slack/…), * even when no operator is actively online right now. Drives the "replies in * a few minutes" status instead of a discouraging "Away". Defaults to true. */ operatorReachable?: boolean; messages: Message[]; /** User identity if identified via PocketPing.identify() */ identity?: UserIdentity; /** Pre-chat form configuration */ preChatForm?: PreChatFormConfig; } /** Pre-chat form configuration */ interface PreChatFormConfig { /** Whether the form is enabled */ enabled: boolean; /** Whether the form is required (can't skip) */ required: boolean; /** When to show the form: 'before-first-message' | 'after-first-message' */ timing: 'before-first-message' | 'after-first-message'; /** What fields to collect: 'email-only' | 'phone-only' | 'email-or-phone' | 'email-and-phone' */ fields: 'email-only' | 'phone-only' | 'email-or-phone' | 'email-and-phone'; /** Whether the form was already completed for this session */ completed: boolean; } /** Pre-chat form submission data */ interface PreChatFormData { email?: string; phone?: string; phoneCountry?: string; } interface PresenceResponse { online: boolean; operators?: Array<{ id: string; name: string; avatar?: string; }>; aiEnabled?: boolean; aiActiveAfter?: number; } type VersionWarningSeverity = 'info' | 'warning' | 'error'; interface VersionWarning { /** Severity level of the warning */ severity: VersionWarningSeverity; /** Human-readable warning message */ message: string; /** Current widget version */ currentVersion: string; /** Minimum supported version (if applicable) */ minVersion?: string; /** Latest available version (if applicable) */ latestVersion?: string; /** Whether the widget should still function */ canContinue: boolean; /** URL to upgrade instructions */ upgradeUrl?: string; } /** Custom event sent from widget to backend or vice versa */ interface CustomEvent { /** Event name (e.g., 'clicked_pricing', 'show_offer') */ name: string; /** Event payload */ data?: Record; /** Timestamp of the event */ timestamp: string; } /** Handler for custom events */ type CustomEventHandler = (data: Record | undefined, event: CustomEvent) => void; /** Options for trigger() method */ interface TriggerOptions { /** If provided, opens the widget and shows this message */ widgetMessage?: string; } /** Tracked element configuration (for SaaS auto-tracking) */ interface TrackedElement { /** CSS selector for the element(s) to track */ selector: string; /** DOM event to listen for (default: 'click') */ event?: 'click' | 'submit' | 'focus' | 'change' | 'mouseenter'; /** Event name sent to backend */ name: string; /** If provided, opens widget with this message when triggered */ widgetMessage?: string; /** Additional data to send with the event */ data?: Record; } /** * User identity data for identifying visitors * @example * PocketPing.identify({ * id: 'user_123', * email: 'john@example.com', * name: 'John Doe', * plan: 'pro', * company: 'Acme Inc' * }) */ interface UserIdentity { /** Required unique user identifier */ id: string; /** User's email address */ email?: string; /** User's display name */ name?: string; /** Any custom fields (plan, company, etc.) */ [key: string]: unknown; } type ResolvedPocketPingConfig = Omit & { endpoint: string; }; type Listener = (data: T) => void; declare class PocketPingClient { private config; private session; private ws; private sse; private isOpen; private listeners; private customEventHandlers; private reconnectAttempts; private maxReconnectAttempts; private reconnectTimeout; private pollingTimeout; private pollingFailures; private maxPollingFailures; private wsConnectedAt; private quickFailureThreshold; private sseConnectionTimeout; private connectionMode; private trackedElementCleanups; private currentTrackedElements; private inspectorMode; private inspectorCleanup; private connectedAt; private disconnectNotified; private boundHandleUnload; private boundHandleVisibilityChange; constructor(config: ResolvedPocketPingConfig); connect(): Promise; disconnect(): void; sendMessage(content: string, attachmentIds?: string[], replyTo?: string): Promise; /** * Upload a file attachment * Returns the attachment data after successful upload * @param file - File object to upload * @param onProgress - Optional callback for upload progress (0-100) * @example * const attachment = await PocketPing.uploadFile(file, (progress) => { * console.log(`Upload ${progress}% complete`) * }) * await PocketPing.sendMessage('Check this file', [attachment.id]) */ uploadFile(file: File, onProgress?: (progress: number) => void): Promise; /** * Upload multiple files at once * @param files - Array of File objects to upload * @param onProgress - Optional callback for overall progress (0-100) * @returns Array of uploaded attachments */ uploadFiles(files: File[], onProgress?: (progress: number) => void): Promise; /** * Upload file to presigned URL with progress tracking */ private uploadToPresignedUrl; fetchMessages(after?: string): Promise; sendTyping(isTyping?: boolean): Promise; sendReadStatus(messageIds: string[], status: MessageStatus): Promise; /** * Edit a message (visitor can only edit their own messages) * @param messageId - ID of the message to edit * @param content - New content for the message */ editMessage(messageId: string, content: string): Promise; /** * Delete a message (soft delete - visitor can only delete their own messages) * @param messageId - ID of the message to delete */ deleteMessage(messageId: string): Promise; getPresence(): Promise; /** * Identify the current user with metadata * Call after user logs in or when user data becomes available * @param identity - User identity data with required id field * @example * PocketPing.identify({ * id: 'user_123', * email: 'john@example.com', * name: 'John Doe', * plan: 'pro', * company: 'Acme Inc' * }) */ identify(identity: UserIdentity): Promise; /** * Submit pre-chat form data (email and/or phone) * @param data - Form data containing email and/or phone */ submitPreChat(data: PreChatFormData): Promise; /** * Submit a CSAT rating for the current conversation. * @param score - 1..5 * @param comment - optional free-text feedback */ submitCsat(score: number, comment?: string): Promise; /** * Reset the user identity and optionally start a new session * Call on user logout to clear user data * @param options - Optional settings: { newSession: boolean } */ reset(options?: { newSession?: boolean; }): Promise; /** * Get the current user identity * @returns UserIdentity or null if not identified */ getIdentity(): UserIdentity | null; getSession(): Session | null; getMessages(): Message[]; isConnected(): boolean; isWidgetOpen(): boolean; getConfig(): ResolvedPocketPingConfig; setOpen(open: boolean): void; toggleOpen(): void; on(event: string, listener: Listener): () => void; private emit; /** * Trigger a custom event to the backend * @param eventName - The name of the event (e.g., 'clicked_pricing', 'viewed_demo') * @param data - Optional payload to send with the event * @param options - Optional trigger options (widgetMessage to open chat) * @example * // Silent event (just notify bridges) * PocketPing.trigger('clicked_cta', { button: 'signup' }) * * // Open widget with message * PocketPing.trigger('clicked_pricing', { plan: 'pro' }, { widgetMessage: 'Need help choosing a plan?' }) */ trigger(eventName: string, data?: Record, options?: TriggerOptions): void; /** * Subscribe to custom events from the backend * @param eventName - The name of the event to listen for (e.g., 'show_offer', 'open_chat') * @param handler - Callback function when event is received * @returns Unsubscribe function * @example * const unsubscribe = PocketPing.onEvent('show_offer', (data) => { * showPopup(data.message) * }) */ onEvent(eventName: string, handler: CustomEventHandler): () => void; /** * Unsubscribe from a custom event * @param eventName - The name of the event * @param handler - The handler to remove */ offEvent(eventName: string, handler: CustomEventHandler): void; private emitCustomEvent; /** * Setup tracked elements from config (used by SaaS dashboard) * @param elements - Array of tracked element configurations */ setupTrackedElements(elements: TrackedElement[]): void; /** * Cleanup all tracked element listeners */ private cleanupTrackedElements; /** * Get current tracked elements configuration */ getTrackedElements(): TrackedElement[]; /** * Enable inspector mode for visual element selection * This shows an overlay that allows clicking on elements to get their CSS selector */ private enableInspectorMode; /** * Disable inspector mode */ private disableInspectorMode; /** * Check if inspector mode is active */ isInspectorModeActive(): boolean; private connectRealtime; private connectWebSocket; private connectSSE; private handleWsFailure; private handleRealtimeEvent; private getLastEventTimestamp; private handleWebSocketEvent; private handleVersionWarning; private scheduleReconnect; private startPolling; private stopPolling; private setupUnloadListeners; private cleanupUnloadListeners; /** * Notify backend that visitor is leaving * Uses sendBeacon for reliability on page unload */ private notifyDisconnect; /** * Send visibility ping to backend (for inactivity tracking) */ private sendVisibilityPing; private fetch; private checkVersionHeaders; private getOrCreateVisitorId; private getStoredSessionId; private storeSessionId; private getStoredIdentity; private storeIdentity; private clearIdentity; private generateId; private pollScreenshotRequests; /** * Handle screenshot request from operator * @param data.silent - If true, screenshot won't appear in widget chat (only sent to bridges) */ private handleScreenshotRequest; /** * Capture screenshot of the viewport using html2canvas */ private captureScreenshot; /** * Dynamically load html2canvas from CDN */ private loadHtml2Canvas; } declare function init(config: PocketPingConfig): PocketPingClient; declare function destroy(): void; declare function open(): void; declare function close(): void; declare function toggle(): void; declare function sendMessage(content: string, attachmentIds?: string[]): Promise; /** * Upload a file attachment * Returns the attachment data after successful upload * @param file - File object to upload * @param onProgress - Optional callback for upload progress (0-100) * @example * const attachment = await PocketPing.uploadFile(file, (progress) => { * console.log(`Upload ${progress}% complete`) * }) * await PocketPing.sendMessage('Check this file', [attachment.id]) */ declare function uploadFile(file: File, onProgress?: (progress: number) => void): Promise; /** * Upload multiple files at once * @param files - Array of File objects to upload * @param onProgress - Optional callback for overall progress (0-100) * @returns Array of uploaded attachments * @example * const attachments = await PocketPing.uploadFiles(files) * const ids = attachments.map(a => a.id) * await PocketPing.sendMessage('Here are the files', ids) */ declare function uploadFiles(files: File[], onProgress?: (progress: number) => void): Promise; /** * Trigger a custom event to the backend * @param eventName - The name of the event (e.g., 'clicked_pricing', 'viewed_demo') * @param data - Optional payload to send with the event * @param options - Optional trigger options (widgetMessage to open chat) * @example * // Silent event (just notify bridges) * PocketPing.trigger('clicked_cta', { button: 'signup' }) * * // Open widget with message * PocketPing.trigger('clicked_pricing', { plan: 'pro' }, { widgetMessage: 'Need help choosing?' }) */ declare function trigger(eventName: string, data?: Record, options?: TriggerOptions): void; /** * Setup tracked elements for auto-tracking (typically called by SaaS backend) * @param elements - Array of tracked element configurations * @example * PocketPing.setupTrackedElements([ * { selector: '#search-btn', event: 'click', name: 'clicked_search' }, * { selector: '.pricing-card', event: 'click', name: 'viewed_pricing', widgetMessage: 'Need help?' } * ]) */ declare function setupTrackedElements(elements: TrackedElement[]): void; /** * Get current tracked elements configuration */ declare function getTrackedElements(): TrackedElement[]; /** * Subscribe to custom events from the backend * @param eventName - The name of the event to listen for * @param handler - Callback function when event is received * @returns Unsubscribe function * @example * const unsubscribe = PocketPing.onEvent('show_offer', (data) => { * showPopup(data.message) * }) */ declare function onEvent(eventName: string, handler: CustomEventHandler): () => void; /** * Unsubscribe from a custom event * @param eventName - The name of the event * @param handler - The handler to remove */ declare function offEvent(eventName: string, handler: CustomEventHandler): void; /** * Identify the current user with metadata * Call after user logs in or when user data becomes available * @param identity - User identity data with required id field * @example * PocketPing.identify({ * id: 'user_123', * email: 'john@example.com', * name: 'John Doe', * plan: 'pro', * company: 'Acme Inc' * }) */ declare function identify(identity: UserIdentity): Promise; /** * Reset the user identity and optionally start a new session * Call on user logout to clear user data * @param options - Optional settings: { newSession: boolean } * @example * // Clear identity but keep session * PocketPing.reset() * * // Clear everything and start fresh * PocketPing.reset({ newSession: true }) */ declare function reset(options?: { newSession?: boolean; }): Promise; /** * Get the current user identity * @returns UserIdentity or null if not identified */ declare function getIdentity(): UserIdentity | null; /** * Subscribe to internal widget events * @param eventName - Event name: 'versionWarning', 'message', 'connect', 'typing', etc. * @param handler - Callback function * @returns Unsubscribe function * @example * PocketPing.on('versionWarning', (warning) => { * if (warning.severity === 'error') { * showUpgradeNotice(warning.message); * } * }) */ declare function on(eventName: string, handler: (data: T) => void): () => void; declare const _default: { init: typeof init; destroy: typeof destroy; open: typeof open; close: typeof close; toggle: typeof toggle; sendMessage: typeof sendMessage; uploadFile: typeof uploadFile; uploadFiles: typeof uploadFiles; trigger: typeof trigger; onEvent: typeof onEvent; offEvent: typeof offEvent; on: typeof on; identify: typeof identify; reset: typeof reset; getIdentity: typeof getIdentity; setupTrackedElements: typeof setupTrackedElements; getTrackedElements: typeof getTrackedElements; }; export { type Attachment, type CustomEvent, type CustomEventHandler, type Message, type PocketPingConfig, type ReplyToData, type TrackedElement, type TriggerOptions, type UserIdentity, type VersionWarning, close, _default as default, destroy, getIdentity, getTrackedElements, identify, init, offEvent, on, onEvent, open, reset, sendMessage, setupTrackedElements, toggle, trigger, uploadFile, uploadFiles };