declare const win: (Window & typeof globalThis) | undefined; /** * Extension kinds that can be lazy loaded */ export type VTiltExtensionKind = "recorder" | "web-vitals" | "chat" | "outbound"; /** * Interface for the lazy-loaded outbound surface (set by outbound.ts). * * Only non-underscore names may appear here: each entrypoint is mangled * independently, so a `_`-prefixed member would get a different name in * array.js than in outbound.js and resolve to undefined at runtime. */ export interface LazyLoadedOutboundInterface { start: () => void; stop: () => void; } /** * Interface for lazy-loaded session recording (set by recorder.ts) * Matches LazyLoadedSessionRecordingInterface in session-recording-wrapper.ts */ export interface LazyLoadedSessionRecordingInterface { start: (startReason?: string) => void; stop: () => void; sessionId: string; status: string; isStarted: boolean; log: (message: string, level: "log" | "warn" | "error") => void; updateConfig: (config: any) => void; /** Sync batch $current_url after SPA $pageview / history nav. */ setLastHref?: (href: string) => void; } /** * Web Vitals metric callback type */ export type WebVitalsMetricCallback = (metric: any) => void; /** * Options for web vitals callbacks */ export interface WebVitalsCallbackOptions { /** Report all changes (useful for CLS which updates over time) */ reportAllChanges?: boolean; } /** * Web Vitals callback function type (with optional options) */ export type WebVitalsCallbackFn = (callback: WebVitalsMetricCallback, options?: WebVitalsCallbackOptions) => void; /** * Web Vitals callbacks interface (set by web-vitals.ts entrypoint) */ export interface WebVitalsCallbacks { onLCP: WebVitalsCallbackFn; onCLS: WebVitalsCallbackFn; onFCP: WebVitalsCallbackFn; onINP: WebVitalsCallbackFn; onTTFB?: WebVitalsCallbackFn; } /** * Chat message structure (matches PostgreSQL chat_messages table) * Note: Read status is determined by cursor comparison, not per-message */ export interface ChatMessage { id: string; channel_id: string; sender_type: "user" | "agent" | "ai" | "system"; sender_id: string | null; sender_name: string | null; sender_avatar_url: string | null; content: string; content_type: "text" | "html" | "attachment"; metadata: Record; /** Immutable creation timestamp — set once on row INSERT. */ created_at: string; /** * Last modification timestamp. Equals `created_at` on a freshly * inserted row; later bumped when the AI streaming finalize writes * the full text onto a placeholder row, or when the (planned) * edit-message feature mutates content. Optional for backwards * compatibility with widget builds that pre-date the field. */ updated_at?: string; /** * Local-only outbound delivery state (widget SDK). Cleared once the * server row is acknowledged. Not returned by API/Ably payloads. */ delivery_status?: "pending" | "sending" | "failed"; } /** * Chat channel structure (maps 1:1 to Ably channel) */ export interface ChatChannel { id: string; project_id: string; person_id: string; distinct_id: string; status: "open" | "closed" | "snoozed"; ai_mode: boolean; unread_count: number; last_message_at: string | null; last_message_preview: string | null; last_message_sender: "user" | "agent" | "ai" | "system" | null; user_last_read_at: string | null; agent_last_read_at: string | null; created_at: string; } /** * Lightweight channel summary for channel list view * Used to avoid loading full channel data until user selects one */ export interface ChatChannelSummary { id: string; status: "open" | "closed" | "snoozed"; ai_mode: boolean; last_message_at: string | null; last_message_preview: string | null; last_message_sender: "user" | "agent" | "ai" | "system" | null; unread_count: number; user_last_read_at: string | null; created_at: string; } /** * Widget view state - determines what UI to show */ export type ChatWidgetView = "list" | "conversation"; /** * Chat widget configuration * * Settings can come from two sources: * 1. Dashboard (fetched from /api/chat/settings) - "snippet-only" mode * 2. Code config (passed to vt.init) - overrides dashboard settings * * This enables Intercom-like flexibility: just add snippet OR customize with code. */ export interface ChatConfig { /** * Enable/disable chat widget. * - undefined: Use dashboard setting (auto-fetch) * - true: Enable (override dashboard) * - false: Disable entirely */ enabled?: boolean; /** * Auto-fetch settings from dashboard (default: true) * When true, SDK fetches /api/chat/settings and uses those as base config. * Code config always overrides fetched settings. */ autoConfig?: boolean; /** Widget position (default: 'bottom-right') */ position?: "bottom-right" | "bottom-left"; /** Widget header/greeting message */ greeting?: string; /** Widget primary color */ color?: string; /** Start in AI mode (default: true) */ aiMode?: boolean; /** AI greeting message (first message from AI) */ aiGreeting?: string; /** Preload widget script on idle vs on-demand */ preload?: boolean; /** Custom theme */ theme?: ChatTheme; /** Offline message shown when business is unavailable */ offlineMessage?: string; /** Collect email when offline */ collectEmailOffline?: boolean; /** Bubble appearance and behavior */ bubble?: BubbleConfig; /** Called when widget is opened */ onWidgetOpen?: () => void; /** Called when widget is closed */ onWidgetClose?: (data: { timeOpenSeconds: number; messagesSent: number; }) => void; /** Called when a new conversation is started */ onConversationStart?: (data: { channelId: string; aiMode: boolean; }) => void; /** Called when user sends a message */ onMessageSent?: (data: { channelId: string; messageId: string; }) => void; /** * Called when the user message row is persisted on the server (before * the AI stream finishes). Use for form → chat → redirect flows. */ onMessageDelivered?: (data: { channelId: string; messageId: string; }) => void; /** Called when a message is received (from AI or agent) */ onMessageReceived?: (data: { channelId: string; messageId: string; senderType: "ai" | "agent"; }) => void; } /** * Chat theme customization */ export interface ChatTheme { primaryColor?: string; fontFamily?: string; borderRadius?: string; headerBgColor?: string; userBubbleColor?: string; agentBubbleColor?: string; } /** * First argument to `vt.sendChatMessage()` / {@link LazyLoadedChatInterface.sendMessage}. * * Plain strings stay plain text for backward compatibility. Use `{ markdown: '...' }`, * `{ html: '...' }`, or `options.format` for rich content. */ export type SendChatMessageContent = string | { text: string; } | { markdown: string; } | { html: string; }; /** * Options for `vt.sendChatMessage()` / {@link LazyLoadedChatInterface.sendMessage}. */ export interface SendChatMessageOptions { /** * Target conversation: `'new'` creates a channel, a UUID selects an existing one, * omit to use the active conversation (must already be in conversation view). */ channel?: "new" | string; /** * Open the widget panel before sending. Default: true. Pass `open: false` to send without opening. */ open?: boolean; /** * Applies when the first argument is a plain string. Default: `text`. * `markdown` and `html` use the same sanitized rendering as AI/agent messages. */ format?: "text" | "markdown" | "html"; /** * Marks the message as automatically injected by your site (form submit, CTA, * etc.) rather than typed by the visitor in the widget. Stored on the message * as `message_source` metadata so the AI can respond appropriately — e.g. * `source: "landing_form"`. Slug-like: letters, numbers, `_`, `-`, max 64 chars. */ source?: string; } /** * Pixel inset of the chat bubble from the viewport edges. * Use to clear host floating buttons, cookie banners, or mobile tab bars. */ export interface BubbleOffset { /** Distance from the bottom edge (default: 20) */ bottom?: number; /** Distance from the right edge when `position` is `bottom-right` (default: 20) */ right?: number; /** Distance from the left edge when `position` is `bottom-left` (default: 20) */ left?: number; } /** * Chat bubble appearance and behavior configuration */ export interface BubbleConfig { /** Allow user to drag the bubble to reposition (default: false) */ draggable?: boolean; /** Show the bubble on load (default: true). Set to false to control via vt.chat.show() */ visible?: boolean; /** * Viewport inset in pixels. Prefer this over custom CSS so positioning * survives widget UI updates. Pass `null` in `vt.updateConfig({ chat: { bubble: { offset: null } } })` to reset * to SDK defaults (20px). */ offset?: BubbleOffset | null; } /** * Interface for lazy-loaded chat widget (set by chat.ts entrypoint) */ export interface LazyLoadedChatInterface { readonly isOpen: boolean; readonly isConnected: boolean; readonly isLoading: boolean; readonly unreadCount: number; readonly channel: ChatChannel | null; readonly channels: ChatChannelSummary[]; readonly currentView: ChatWidgetView; open(): void; close(): void; toggle(): void; show(): void; hide(): void; /** Fetch/refresh the list of user's channels */ getChannels(): Promise; /** Select a channel and load its messages */ selectChannel(channelId: string): Promise; /** Create a new channel and enter it */ createChannel(): Promise; /** Go back to channel list from conversation view */ goToChannelList(): void; /** * Tier A closed-badge: apply aggregate unread from `GET /api/chat/widget/unread`. * No-op while the panel is open. */ applyPolledUnreadCount?(count: number): void; sendMessage(content: SendChatMessageContent, options?: SendChatMessageOptions): Promise; markAsRead(): void; onMessage(callback: (message: ChatMessage) => void): () => void; onTyping(callback: (isTyping: boolean, senderType: string) => void): () => void; onConnectionChange(callback: (connected: boolean) => void): () => void; updateConfig?(config: ChatConfig): void; registerWidget?(definition: import("../extensions/chat/widget-registry").ChatWidgetDefinition): void; destroy(): void; } /** * VTilt Extensions interface for dynamically loaded modules * This is the contract between lazily loaded extensions and the SDK */ export interface VTiltExtensions { /** Load an external dependency script */ loadExternalDependency?: (instance: any, // VTilt instance - using any to avoid circular imports kind: VTiltExtensionKind, callback: (error?: string | Event, event?: Event) => void) => void; /** rrweb record function (set by recorder.ts) */ rrweb?: { record: any; version?: string; }; /** rrweb plugins (set by recorder.ts) */ rrwebPlugins?: { getRecordConsolePlugin?: () => any; getRecordNetworkPlugin?: (options: any) => any; }; /** Factory to create LazyLoadedSessionRecording (set by recorder.ts) */ initSessionRecording?: (instance: any, config?: any) => LazyLoadedSessionRecordingInterface; /** Web Vitals callbacks (set by web-vitals.ts entrypoint) */ webVitalsCallbacks?: WebVitalsCallbacks; /** Factory to create LazyLoadedChat (set by chat.ts entrypoint) */ initChat?: (instance: any, config?: ChatConfig) => LazyLoadedChatInterface; /** Factory to create the outbound surface (set by outbound.ts entrypoint) */ initOutbound?: (instance: any) => LazyLoadedOutboundInterface; } export type AssignableWindow = Window & typeof globalThis & { /** Main VTilt instance */ vt: any; /** VTilt Extensions for dynamically loaded modules */ __VTiltExtensions__?: VTiltExtensions; }; export declare const ArrayProto: any[]; export declare const nativeForEach: (callbackfn: (value: any, index: number, array: any[]) => void, thisArg?: any) => void; export declare const nativeIndexOf: (searchElement: any, fromIndex?: number) => number; export declare const navigator: Navigator | undefined; export declare const document: Document | undefined; export declare const location: Location | undefined; export declare const fetch: typeof globalThis.fetch | undefined; /** * Whether we are running in a browser environment. * Safe to call in SSR / Web Workers / Node.js. */ export declare const isBrowser: boolean; export declare const XMLHttpRequest: { new (): XMLHttpRequest; prototype: XMLHttpRequest; readonly UNSENT: 0; readonly OPENED: 1; readonly HEADERS_RECEIVED: 2; readonly LOADING: 3; readonly DONE: 4; } | undefined; export declare const AbortController: { new (): AbortController; prototype: AbortController; } | undefined; export declare const userAgent: string | undefined; export declare const assignableWindow: AssignableWindow; export { win as window };