/** * chatlist.ts — standalone chat list widget. * * Completely separate from mount() / the chat widget. * Shows all conversations for a given userId / guest on a profile. * Tapping a row fires onSelect(entry) — the caller decides what to do * (navigate to a new page, open a ChatWidget inline, etc.) * * Usage (vanilla): * import { mountChatList } from '@paramms/chat-widget/chatlist' * const handle = mountChatList({ * el: document.getElementById('chat-list'), * url: 'https://api.relay.paramms.com', // ONE url, any scheme * profileId: 'p_usedcars', * userId: currentUser.id, // optional — uses localStorage UID if omitted * onSelect: (entry) => { * window.location.href = `/listings/${entry.subjectId}#chat` * }, * }) * handle.refresh() // manually re-fetch the list * handle.close() // unmount and clean up */ export interface ChatListEntry { id: string; /** Chatroom this conversation belongs to. With `scope: 'tenant'` this can * differ from the profileId the list was mounted with — open the chat * against THIS profileId. */ profileId?: string; /** 'support' (default) or 'direct' (user↔user DM). */ kind?: string; /** For direct conversations: the other participant's user id. */ peerId?: string; subjectId?: string; subjectTitle?: string; /** One-line detail — e.g. "45,000 km · Auto" */ subjectMeta?: string; /** URL of the listing/item page — stored automatically when the widget first opens */ subjectUrl?: string; state: string; updatedAt: number; lastSeq?: number; lastMessage?: string; } export interface ChatListOptions { /** Mount target element */ el: HTMLElement; /** Relay URL — ONE url, any scheme (https recommended). The WebSocket URL * and REST base are derived automatically. */ url: string; /** HTTP(S) base for REST — only when REST is on a different origin. * @deprecated pass a single `url`; kept for back-compat. */ apiUrl?: string; /** Profile ID to scope conversations to */ profileId?: string; /** Tenant-level identification — list EVERY conversation this user has * with the business, across ALL of its chatrooms, without naming one * (e.g. a platform running a marketplace chatroom AND a general-support * chatroom). Provide `profileId` OR `tenantId` (profileId wins if both; * it also fixes where "new conversation" opens). With only `tenantId`, * the compose target is the server-reported defaultProfileId (the * tenant's oldest chatroom). */ tenantId?: string; /** A signed identity token (ES256 JWT) — the production identity tier for * chatrooms with signed identity enabled. Wins over `userId`. */ token?: string; /** Your logged-in user's stable ID. Omit for anonymous (uses localStorage UID) */ userId?: string; /** Which conversations to list (default 'profile'): * 'profile' — only this chatroom's threads. * 'tenant' — every conversation this user has with the chatroom's owning * business, across ALL of its chatrooms (a real chat-app inbox). Rows * carry `profileId` so each opens against the right chatroom. */ scope?: 'profile' | 'tenant'; /** Called when the user taps a conversation row */ onSelect: (entry: ChatListEntry) => void; /** When provided, the list shows a ✎ compose button in the header (and a * "Start a conversation" button in the empty state) that calls this — * wire it to open a fresh/general thread. Without it a user with no * conversations yet has nothing to tap. */ onNewChat?: () => void; /** Set when the CALLER draws its own close (✕) control overlaying the list's * top-right corner — reserves header space so it doesn't sit on top of the * ✎ compose button. */ reserveCloseSpace?: boolean; /** Brand colour hex — default '#6c5ce7' */ accent?: string; theme?: 'auto' | 'light' | 'dark'; webfont?: boolean; /** UI language for built-in strings ('ko', 'ko-KR', …). Omit to auto-detect * from the visitor's browser. Korean ⇄ English; non-Korean → English. */ locale?: string; /** i18n overrides */ i18n?: { title?: string; search?: string; empty?: string; unread?: string; all?: string; error?: string; retry?: string; close?: string; newChat?: string; startChat?: string; }; } export interface ChatListHandle { /** Re-fetch and re-render the list */ refresh(): void; /** Unmount and clean up */ close(): void; /** Where "new conversation" should open: the configured profileId, else the * server-reported tenant default (oldest chatroom). Undefined until the * first successful fetch when only tenantId was configured. */ defaultProfileId(): string | undefined; } /** Mount a standalone chat list widget. */ export declare function mountChatList(opts: ChatListOptions): ChatListHandle;