/** * ChatStore — signal-based state container for the chat widget. * * All UI state (view, messages, channels, typing) and all realtime state * (connection state, channel ids) live here as @preact/signals signals. * Components read `.value` inside JSX; Preact's signal integration re-renders * only the affected subtree. * * Mutations are direct assignments (`store.isOpen.value = true`) from the * controller or the Ably handler layer. The store itself has no I/O and no * DOM dependency, which makes the rest of the widget trivially testable. * * See: docs/modules/chat/widget-architecture.md */ import type { ChatChannel, ChatChannelSummary, ChatMessage, ChatTheme, ChatWidgetView } from "../../../utils/globals"; /** Realtime connection state vocabulary the store exposes to UI. */ export type RealtimeConnectionState = "disconnected" | "connecting" | "connected" | "suspended" | "failed"; /** Banner state derived from connection state. */ export type ConnectionBanner = "none" | "reconnecting" | "failed"; /** Default theme — Inter is vTilt's brand font; system stack as fallback. */ export declare const DEFAULT_THEME: Required>; export declare class ChatStore { isOpen: import("@preact/signals").Signal; isVisible: import("@preact/signals").Signal; isLoading: import("@preact/signals").Signal; currentView: import("@preact/signals").Signal; channels: import("@preact/signals").Signal; channel: import("@preact/signals").Signal; messages: import("@preact/signals").Signal; isTyping: import("@preact/signals").Signal; typingSender: import("@preact/signals").Signal; typingSenderType: import("@preact/signals").Signal<"user" | "agent" | "ai" | null>; agentLastReadAt: import("@preact/signals").Signal; initialUserReadAt: import("@preact/signals").Signal; userScrolledUp: import("@preact/signals").Signal; unreadNewMessagesCount: import("@preact/signals").Signal; theme: import("@preact/signals").Signal>>; /** Live connection state from Ably's connection events. */ connectionState: import("@preact/signals").Signal; /** Numeric Ably project id (set after first successful token request). */ ablyProjectId: import("@preact/signals").Signal; /** Channel id the realtime layer has attached to. `null` on list view. */ realtimeChannelId: import("@preact/signals").Signal; /** * True once both the per-conversation main + typing Ably channels have * successfully attached for `realtimeChannelId`. Flips back to `false` * on detach, attach failure, or identity change. Drives the "Connecting…" * indicator in the conversation view. */ realtimeAttached: import("@preact/signals").Signal; /** * Channel id whose details we are currently fetching. Guards against rapid * navigation: a slow GET resolver checks `pendingRealtimeChannelId === id` * before writing into the store. */ pendingRealtimeChannelId: import("@preact/signals").Signal; /** True while `handleIdentityChange()` is tearing down and rebuilding. */ identityChangeInFlight: import("@preact/signals").Signal; /** * Distinct id the live Ably connection was authorised for. Used by the * `connected` event handler to detect drift after `vt.identify()`. */ lastConnectedDistinctId: import("@preact/signals").Signal; /** Max distinct channels to keep in the cache. LRU-trimmed on write. */ private static readonly MESSAGES_CACHE_LIMIT; private _messagesCache; isConnected: import("@preact/signals").ReadonlySignal; /** * Tier A aggregate from `GET /api/chat/widget/unread` while the panel is * closed. Cleared on `open()` so channel rows own the badge again. */ polledUnreadTotal: import("@preact/signals").Signal; /** Sum of unread across all channels — drives the bubble badge. */ unreadCount: import("@preact/signals").ReadonlySignal; /** Banner shown above the input when realtime is degraded. */ connectionBanner: import("@preact/signals").ReadonlySignal; /** * True when the realtime transport is fully ready for the *currently * displayed* conversation: connection is `connected`, the channel object * exists, and the realtime layer has attached the per-conversation * channels for it. Used by the UI to show a subtle "Connecting…" pill * while realtime is still spinning up after navigation or identify. */ realtimeReady: import("@preact/signals").ReadonlySignal; /** * Replace `messages` only if `next` differs by identity from current array. * Signals trigger re-renders only on identity change of the value, so we * always pass a new array to ensure subscribers re-evaluate. */ setMessages(next: ChatMessage[]): void; /** Patch a single channel summary in place, then re-sort by last_message_at. */ patchChannelSummary(id: string, patch: Partial): void; /** Insert a channel summary at the top of the list (skip if id exists). */ prependChannelSummary(summary: ChatChannelSummary): void; /** Reset all realtime / channel-scoped state. Used on goToChannelList() * and `_handleIdentityChange()` — places where we leave the conversation * entirely. For *between-channel* transitions, prefer * `prepareChannelSwitch()` which leaves the realtime signals tracking * the last good attach so `realtimeReady` doesn't blink false. */ clearActiveChannel(): void; /** Soft reset between channels — clears conversation-scoped state but * leaves `realtimeChannelId` / `realtimeAttached` tracking the previous * successful attach. `AblyClient.attachConversation()` atomically swaps * those signals once the new channel is fully attached, so * `realtimeReady` (and therefore the "Connecting…" banner) doesn't * flicker during normal channel switches. * * Inbound stale frames from the previous channel are already filtered * by AblyClient's channel-ref guard, so leaving these signals true is * safe from a correctness standpoint. */ prepareChannelSwitch(): void; /** * Cache the message list for a channel so a later `selectChannel(id)` * can paint instantly. Drops `temp-*` rows (they don't belong to a * persisted state) and LRU-trims to `MESSAGES_CACHE_LIMIT`. */ cacheMessages(channelId: string, messages: ChatMessage[]): void; /** Returns the cached message list for a channel, or null if absent. */ getCachedMessages(channelId: string): ChatMessage[] | null; /** Drop a channel from the cache. Used on full identity reset. */ clearMessagesCache(): void; }