@realtimexsco/live-chat 1.3.1 → 1.3.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,1853 +1,73 @@
1
1
  # @realtimexsco/live-chat
2
2
 
3
- > Complete real-time chat UI for **React** and **Next.js**DMs, groups, reactions, replies, forwarding, pins, stars, attachments, search, light/dark theme, locale/timezone, and **full UI customization** (replace the whole chat or any single component).
3
+ > Drop-in real-time chat UI for React and Next.js — direct messages, group chats, reactions, replies, forwarding, pinned/starred messages, and more. Built on Socket.IO, Zustand, Tailwind CSS v4, and Radix UI.
4
+
5
+ [![npm version](https://img.shields.io/npm/v/@realtimexsco/live-chat.svg)](https://www.npmjs.com/package/@realtimexsco/live-chat)
6
+ [![npm downloads](https://img.shields.io/npm/dm/@realtimexsco/live-chat.svg)](https://www.npmjs.com/package/@realtimexsco/live-chat)
7
+ [![license](https://img.shields.io/npm/l/@realtimexsco/live-chat.svg)](./LICENSE)
4
8
 
5
9
  | | |
6
10
  |---|---|
7
- | **Package name** | `@realtimexsco/live-chat` |
8
- | **Registry** | [npm](https://www.npmjs.com/) (public — no `.npmrc` required) |
9
- | **Styles entry** | `@realtimexsco/live-chat/styles.css` |
11
+ | **Package** | [`@realtimexsco/live-chat`](https://www.npmjs.com/package/@realtimexsco/live-chat) |
12
+ | **Styles** | `@realtimexsco/live-chat/styles.css` |
10
13
  | **Requires** | React 17+, Tailwind CSS **v4**, Zustand 4+ |
11
- | **Current version** | `1.1.0` |
12
-
13
- Socket.IO client, REST API client, Zustand store, Radix UI, emoji picker, and all chat UI are **bundled inside the package**. Your app only installs a few peer dependencies.
14
-
15
- ---
16
-
17
- ## Start here (5-minute overview)
18
-
19
- 1. **Install** the package + peers (`react`, `react-dom`, `zustand`, `tailwindcss` v4).
20
- 2. **Import CSS** — `@import "@realtimexsco/live-chat/styles.css"` and add Tailwind `@source` for the package `dist` files.
21
- 3. **Create** `chat.config.ts` with `clientId`, `accessToken`, `loggedUserDetails`, `apiUrl`, `socketUrl`.
22
- 4. **Render** `<ChatMain {...chatConfig} />` — the package includes a built-in viewport wrapper. For admin templates use `viewportHeight="calc(100dvh - 4rem)"`; for full-page chat use `viewportHeight="screen"`.
23
- 5. **Optional** — pass `components`, `classNames`, or `features` to customize. **Omit any slot → package default is used automatically.**
24
-
25
- ```tsx
26
- // Full page
27
- <ChatMain {...chatConfig} viewportHeight="screen" />
28
-
29
- // Admin shell with 4rem header (Elstar, Metronic, etc.)
30
- <ChatMain {...chatConfig} viewportHeight="calc(100dvh - 4rem)" />
31
-
32
- // Inside your own bounded flex column (parent must have height + min-h-0)
33
- <div className="flex min-h-0 flex-1 overflow-hidden">
34
- <ChatMain {...chatConfig} viewportHeight="full" />
35
- </div>
36
- ```
37
-
38
- **Do not** pass empty or placeholder components for slots you want to keep default — simply omit those keys from `components`.
39
-
40
- ---
41
-
42
- ## Table of contents
43
-
44
- 1. [Chat features included](#chat-features-included)
45
- 2. [What you install vs what is bundled](#what-you-install-vs-what-is-bundled)
46
- 3. [Backend credentials](#backend-credentials)
47
- 4. [Installation step by step](#installation-step-by-step)
48
- 5. [Tailwind CSS v4 setup (required)](#tailwind-css-v4-setup-required)
49
- 6. [Project config file](#project-config-file)
50
- 7. [Quick start — minimum working chat](#quick-start--minimum-working-chat)
51
- 8. [Complete ChatMain example](#complete-chatmain-example)
52
- 9. [Customization — three ways](#customization--three-ways)
53
- 10. [Default component fallback (important)](#default-component-fallback-important)
54
- 11. [Which slots are wired](#which-slots-are-wired)
55
- 12. [Production integration pattern (admin templates)](#production-integration-pattern-admin-templates)
56
- 13. [Customization examples (Case 1–3)](#customization-examples-case-13)
57
- 14. [Phone & video calling](#phone--video-calling)
58
- 15. [Theme, light/dark, date/time](#theme-lightdark-datetime)
59
- 16. [Layout sections (`layout` prop)](#layout-sections-layout-prop)
60
- 17. [All component slots + examples](#all-component-slots--examples)
61
- 18. [All classNames keys](#all-classnames-keys)
62
- 19. [Wrapping package defaults (`packageDefaultComponents`)](#wrapping-package-defaults-packagedefaultcomponents)
63
- 20. [API query params](#api-query-params)
64
- 21. [ChatMain props reference](#chatmain-props-reference)
65
- 22. [Chat provider (composable mode)](#chat-provider-composable-mode)
66
- 23. [Hooks & store](#hooks--store)
67
- 24. [React (Vite) — full setup](#react-vite--full-setup)
68
- 25. [Next.js — full setup](#nextjs--full-setup)
69
- 26. [Environment variables](#environment-variables)
70
- 27. [Dev proxy (CORS)](#dev-proxy-cors)
71
- 28. [Architecture](#architecture)
72
- 29. [All exports](#all-exports)
73
- 30. [Troubleshooting](#troubleshooting)
74
- 31. [Setup checklist](#setup-checklist)
75
- 32. [Package development (maintainers)](#package-development-maintainers)
76
- 33. [License](#license)
14
+ | **License** | [MIT](./LICENSE) |
77
15
 
78
- ---
79
-
80
- ## Chat features included
81
-
82
- | Feature | Supported |
83
- |---------|-----------|
84
- | Direct messages (1-to-1) | Yes |
85
- | Group chats | Yes |
86
- | Real-time messages (Socket.IO) | Yes |
87
- | Optimistic send (instant UI) | Yes |
88
- | Reactions, replies, forward | Yes |
89
- | Edit / delete (me / everyone) | Yes |
90
- | Pin & star messages | Yes |
91
- | Attachments (images, files) | Yes |
92
- | In-chat message search | Yes |
93
- | Typing indicators | Yes |
94
- | Read receipts (sent / delivered / read) | Yes |
95
- | New chat / user list modal | Yes |
96
- | Group members, admins, permissions | Yes |
97
- | Light / dark mode | Yes |
98
- | Locale & timezone formatting | Yes |
99
- | Phone / video call hooks | Yes (you wire WebRTC) |
100
- | Full UI customization | Yes |
101
-
102
- ---
103
-
104
- ## What you install vs what is bundled
105
-
106
- ### You must install (peer dependencies)
107
-
108
- | Package | Version | When | Why |
109
- |---------|---------|------|-----|
110
- | `react` | ≥ 17 | Always | UI framework |
111
- | `react-dom` | ≥ 17 | Always | DOM rendering |
112
- | `zustand` | ≥ 4 | Always | Chat state store |
113
- | `tailwindcss` | **v4** | Always | Package uses Tailwind utilities |
114
- | `@tailwindcss/vite` | v4 | Vite only | Tailwind plugin |
115
- | `@tailwindcss/postcss` | v4 | Next.js only | Tailwind PostCSS plugin |
116
- | `next` | ≥ 13 | Next.js only | App Router |
117
- | `react-hot-toast` | ≥ 2 | Optional | Only if you use `showNotification` helper |
118
-
119
- ### Bundled inside the package (do not install separately)
120
-
121
- `axios`, `socket.io-client`, `radix-ui`, `lucide-react`, `emoji-picker-react`, `clsx`, `tailwind-merge`, `class-variance-authority`, and all chat components.
122
-
123
- ---
124
-
125
- ## Backend credentials
126
-
127
- | Field | Example | Notes |
128
- |-------|---------|-------|
129
- | `clientId` | `"6a35339216e46130366cda93"` | RealtimeX tenant / client id |
130
- | `accessToken` | JWT string | User access token |
131
- | `loggedUserDetails._id` | `"6a3533c416e46130366cda98"` | **Must match `_id` inside JWT** |
132
- | `loggedUserDetails.name` | `"chat1"` | Display name |
133
- | `loggedUserDetails.email` | `"chat1@example.com"` | Used for client-user login |
134
- | `apiUrl` | `https://realtimex.softwareco.com/api/v1` | REST API — **must end with** `/api/v1` (**required**, pass from your app) |
135
- | `socketUrl` | `https://realtimex.softwareco.com` | Socket host — **no** `/api/v1`; connects at `/connection`. Optional — derived from `apiUrl` if omitted |
136
- | `themeColor` | `"#6366f1"` | Brand accent (optional) |
137
-
138
- > **Important:** If `loggedUserDetails._id` does not match the JWT `_id`, messages may appear on the wrong side, contacts may be empty, or session sync can fail. Decode your JWT or use the id returned from the login API.
16
+ Socket.IO, REST client, Zustand store, Radix UI, emoji picker, and chat UI are **bundled**. Your app only installs peer dependencies.
139
17
 
140
18
  ---
141
19
 
142
- ## Installation step by step
143
-
144
- ### 1. Install the package and peers
145
-
146
- **npm:**
20
+ ## Install
147
21
 
148
22
  ```bash
149
23
  npm install @realtimexsco/live-chat react react-dom zustand tailwindcss
150
24
  ```
151
25
 
152
- **pnpm:**
153
-
154
- ```bash
155
- pnpm add @realtimexsco/live-chat react react-dom zustand tailwindcss
156
- ```
157
-
158
- ### 2. Install Tailwind v4 plugin for your framework
159
-
160
- **Vite:**
161
-
162
- ```bash
163
- pnpm add -D @tailwindcss/vite @vitejs/plugin-react vite
164
- ```
165
-
166
- **Next.js:**
167
-
168
- ```bash
169
- pnpm add -D @tailwindcss/postcss
170
- # next is usually already installed
171
- ```
172
-
173
- ### 3. Verify install
174
-
175
- ```bash
176
- npm ls @realtimexsco/live-chat
177
- ls node_modules/@realtimexsco/live-chat/dist/index.d.ts # TypeScript types must exist
178
- ```
179
-
180
- ### 4. Configure CSS, config file, and chat page (see sections below)
181
-
182
- ### 5. Run your app and open the chat route
183
-
184
- No `.npmrc` or private token is required — the package is on the public npm registry.
185
-
186
- ---
187
-
188
- ## Tailwind CSS v4 setup (required)
189
-
190
- The package will look unstyled without these three steps:
191
-
192
- 1. `@import` Tailwind + package `styles.css`
193
- 2. `@source` the package `dist` files so Tailwind scans bundled classes
194
- 3. Define `--chat-theme*` CSS variables in `:root`
195
-
196
- ### Vite — `src/index.css`
197
-
198
- ```css
199
- @import "tailwindcss";
200
- @import "@realtimexsco/live-chat/styles.css";
201
-
202
- @source "../node_modules/@realtimexsco/live-chat/dist/index.mjs";
203
- @source "../node_modules/@realtimexsco/live-chat/dist/index.cjs";
204
- @source "../**/*.{js,jsx,ts,tsx}";
205
-
206
- :root {
207
- --radius: 0.625rem;
208
- --background: oklch(1 0 0);
209
- --foreground: oklch(0.145 0 0);
210
- --primary: oklch(0.205 0 0);
211
- --primary-foreground: oklch(0.985 0 0);
212
- --muted: oklch(0.97 0 0);
213
- --muted-foreground: oklch(0.556 0 0);
214
- --border: oklch(0.922 0 0);
215
- --ring: oklch(0.708 0 0);
216
- --destructive: oklch(0.577 0.245 27.325);
217
- --card: oklch(1 0 0);
218
- --card-foreground: oklch(0.145 0 0);
219
- --popover: oklch(1 0 0);
220
- --popover-foreground: oklch(0.145 0 0);
221
- --accent: oklch(0.97 0 0);
222
- --accent-foreground: oklch(0.205 0 0);
223
- --secondary: oklch(0.97 0 0);
224
- --secondary-foreground: oklch(0.205 0 0);
225
- --input: oklch(0.922 0 0);
226
-
227
- /* Overridden at runtime by ChatMain `themeColor` */
228
- --chat-theme: #6366f1;
229
- --chat-theme-5: #6366f10D;
230
- --chat-theme-10: #6366f11A;
231
- --chat-theme-20: #6366f133;
232
- --chat-theme-40: #6366f166;
233
- --chat-theme-60: #6366f199;
234
- }
235
-
236
- @theme inline {
237
- --color-background: var(--background);
238
- --color-foreground: var(--foreground);
239
- --color-primary: var(--primary);
240
- --color-primary-foreground: var(--primary-foreground);
241
- --color-muted: var(--muted);
242
- --color-muted-foreground: var(--muted-foreground);
243
- --color-border: var(--border);
244
- --color-ring: var(--ring);
245
- --color-destructive: var(--destructive);
246
- --color-card: var(--card);
247
- --color-card-foreground: var(--card-foreground);
248
- --color-popover: var(--popover);
249
- --color-popover-foreground: var(--popover-foreground);
250
- --color-accent: var(--accent);
251
- --color-accent-foreground: var(--accent-foreground);
252
- --color-secondary: var(--secondary);
253
- --color-secondary-foreground: var(--secondary-foreground);
254
- --color-input: var(--input);
255
- --color-chat-theme: var(--chat-theme);
256
- --color-chat-theme-5: var(--chat-theme-5);
257
- --color-chat-theme-10: var(--chat-theme-10);
258
- --color-chat-theme-20: var(--chat-theme-20);
259
- --color-chat-theme-40: var(--chat-theme-40);
260
- --color-chat-theme-60: var(--chat-theme-60);
261
- }
262
-
263
- @layer base {
264
- html, body, #root { height: 100%; }
265
- body { @apply bg-background text-foreground; }
266
- }
267
- ```
268
-
269
- ### Next.js — `src/app/globals.css`
270
-
271
- > `globals.css` is inside `src/app/`, so `@source` uses **`../../node_modules`** (not `../`).
272
-
273
- ```css
274
- @import "tailwindcss";
275
- @import "@realtimexsco/live-chat/styles.css";
276
-
277
- @source "../../node_modules/@realtimexsco/live-chat/dist/index.mjs";
278
- @source "../../node_modules/@realtimexsco/live-chat/dist/index.cjs";
279
-
280
- /* Copy :root, @theme inline, and @layer base from the Vite example above */
281
- ```
282
-
283
- ### `@source` path cheat sheet
284
-
285
- | CSS file location | `@source` prefix |
286
- |-------------------|------------------|
287
- | `src/index.css` (Vite) | `../node_modules/...` |
288
- | `src/app/globals.css` (Next.js) | `../../node_modules/...` |
289
-
290
- Wrong path = broken or unstyled chat UI.
291
-
292
- ### Vite — `vite.config.ts`
293
-
294
- ```ts
295
- import path from "path";
296
- import { fileURLToPath } from "url";
297
- import { defineConfig } from "vite";
298
- import react from "@vitejs/plugin-react";
299
- import tailwindcss from "@tailwindcss/vite";
300
-
301
- const __dirname = path.dirname(fileURLToPath(import.meta.url));
302
-
303
- export default defineConfig({
304
- plugins: [react(), tailwindcss()],
305
- resolve: {
306
- alias: { "@": path.resolve(__dirname, "src") },
307
- dedupe: ["react", "react-dom", "zustand", "socket.io-client", "@realtimexsco/live-chat"],
308
- },
309
- optimizeDeps: {
310
- exclude: ["@realtimexsco/live-chat"],
311
- include: ["zustand", "socket.io-client"],
312
- },
313
- });
314
- ```
315
-
316
- ### Next.js — `next.config.ts`
317
-
318
- ```ts
319
- import type { NextConfig } from "next";
320
-
321
- const nextConfig: NextConfig = {
322
- transpilePackages: ["@realtimexsco/live-chat"],
323
- };
324
-
325
- export default nextConfig;
326
- ```
327
-
328
- ### Next.js — `postcss.config.mjs`
329
-
330
- ```js
331
- const config = { plugins: { "@tailwindcss/postcss": {} } };
332
- export default config;
333
- ```
26
+ **Vite:** `npm i -D @tailwindcss/vite` · **Next.js:** `npm i -D @tailwindcss/postcss`
334
27
 
335
28
  ---
336
29
 
337
- ## Project config file
338
-
339
- Create `src/configs/chat.config.ts`:
340
-
341
- ```ts
342
- /** Optional: read _id from JWT so it always matches the token */
343
- function userIdFromToken(token: string): string | undefined {
344
- try {
345
- const payload = JSON.parse(
346
- atob(token.split(".")[1].replace(/-/g, "+").replace(/_/g, "/"))
347
- );
348
- return payload._id || payload.id;
349
- } catch {
350
- return undefined;
351
- }
352
- }
353
-
354
- const accessToken = "YOUR_JWT_ACCESS_TOKEN";
355
-
356
- export const chatConfig = {
357
- clientId: "YOUR_CLIENT_ID",
358
- accessToken,
359
- loggedUserDetails: {
360
- _id: userIdFromToken(accessToken) ?? "YOUR_USER_ID",
361
- name: "Your Name",
362
- email: "you@example.com",
363
- },
364
- apiUrl: "https://realtimex.softwareco.com/api/v1",
365
- socketUrl: "https://realtimex.softwareco.com", // optional — auto-derived from apiUrl
366
- themeColor: "#6366f1",
367
- } as const;
368
- ```
369
-
370
- ---
30
+ ## Quick start
371
31
 
372
- ## Quick startminimum working chat
32
+ 1. Import CSS`@import "@realtimexsco/live-chat/styles.css"` and add Tailwind `@source` for the package `dist` files.
33
+ 2. Create `chat.config.ts` with `clientId`, `accessToken`, `loggedUserDetails`, `apiUrl`, `socketUrl`.
34
+ 3. Render `<ChatMain {...chatConfig} />`.
373
35
 
374
36
  ```tsx
375
- "use client"; // Next.js only — required on any file that imports chat
376
-
377
37
  import ChatMain from "@realtimexsco/live-chat";
378
38
  import { chatConfig } from "@/configs/chat.config";
379
39
 
380
40
  export default function ChatPage() {
381
41
  return (
382
42
  <div className="h-dvh min-h-0 overflow-hidden">
383
- <ChatMain
384
- clientId={chatConfig.clientId}
385
- accessToken={chatConfig.accessToken}
386
- loggedUserDetails={chatConfig.loggedUserDetails}
387
- apiUrl={chatConfig.apiUrl}
388
- socketUrl={chatConfig.socketUrl}
389
- themeColor={chatConfig.themeColor}
390
- />
391
- </div>
392
- );
393
- }
394
- ```
395
-
396
- **Container height is required.** `ChatMain` includes a built-in viewport (`chat-package-viewport`). Set `viewportHeight="screen"` for full page, or `viewportHeight="calc(100dvh - 4rem)"` below a fixed header. Alternatively wrap with `h-dvh min-h-0 overflow-hidden` and use default `viewportHeight="full"`.
397
-
398
- ---
399
-
400
- ## Complete ChatMain example
401
-
402
- All common props: theme, locale, layout, customization, calls, alerts.
403
-
404
- ```tsx
405
- "use client";
406
-
407
- import { useState } from "react";
408
- import ChatMain, {
409
- type ChatColorMode,
410
- type ChatComponents,
411
- type ChatClassNames,
412
- type ChatFeatures,
413
- type AvatarSlotProps,
414
- defaultChatLayoutConfig,
415
- } from "@realtimexsco/live-chat";
416
- import { chatConfig } from "@/configs/chat.config";
417
-
418
- const MyAvatar = ({ src, name, size }: AvatarSlotProps) => (
419
- <img
420
- src={src || undefined}
421
- alt={name}
422
- className={`rounded-full object-cover avatar--${size}`}
423
- style={{ width: 36, height: 36 }}
424
- />
425
- );
426
-
427
- const components: ChatComponents = {
428
- Avatar: MyAvatar,
429
- // ChatLayout: MyFullCustomUI, // Case 1 — replace entire UI
430
- // MessageItem: MyMessageBubble, // Case 2 — single slot
431
- };
432
-
433
- const classNames: ChatClassNames = {
434
- sidebar: "bg-slate-950",
435
- channelHeader: "border-b-2 border-indigo-500",
436
- messageBubbleSender: "rounded-3xl shadow-md",
437
- messageBubbleReceiver: "rounded-3xl",
438
- };
439
-
440
- const features: ChatFeatures = {
441
- showPhoneCall: true,
442
- showVideoCall: true,
443
- onPhoneCall: ({ activeChannel, conversationId }) => {
444
- console.log("Voice call", activeChannel.name, conversationId);
445
- },
446
- onVideoCall: ({ activeChannel, conversationId }) => {
447
- console.log("Video call", activeChannel.name, conversationId);
448
- },
449
- };
450
-
451
- export default function ChatPage() {
452
- const [colorMode, setColorMode] = useState<ChatColorMode>("light");
453
- const [error, setError] = useState<string | null>(null);
454
-
455
- return (
456
- <div className="h-dvh min-h-0 overflow-hidden">
457
- <ChatMain
458
- clientId={chatConfig.clientId}
459
- accessToken={chatConfig.accessToken}
460
- loggedUserDetails={chatConfig.loggedUserDetails}
461
- apiUrl={chatConfig.apiUrl}
462
- socketUrl={chatConfig.socketUrl}
463
- themeColor={chatConfig.themeColor}
464
- colorMode={colorMode}
465
- onColorModeChange={setColorMode}
466
- showColorModeToggle={false}
467
- locale="en-US"
468
- timeZone="Asia/Kolkata"
469
- hour12={false}
470
- showDateTimeSettings
471
- layout={{ ...defaultChatLayoutConfig }}
472
- components={components}
473
- classNames={classNames}
474
- features={features}
475
- error={error}
476
- onErrorDismiss={() => setError(null)}
477
- />
478
- </div>
479
- );
480
- }
481
- ```
482
-
483
- ---
484
-
485
- ## Customization — three ways
486
-
487
- You can combine all three.
488
-
489
- | Case | Prop | What it does |
490
- |------|------|--------------|
491
- | **1** | `components.ChatLayout` | Replace the **entire** chat UI shell |
492
- | **2** | `components.*` | Replace **one** component (avatar, header, message bubble, drawer, etc.) |
493
- | **3** | `classNames.*` | Override **Tailwind classes** per region — no new React components |
494
-
495
- Omit any slot or className key → **package default is used automatically**.
496
-
497
- ---
498
-
499
- ## Default component fallback (important)
500
-
501
- Customization is **opt-in per slot**. You never need to register every component.
502
-
503
- ### How it works internally
504
-
505
- ```
506
- ChatMain components={{ MembersDrawer: MyDrawer }}
507
-
508
- ChatCustomizationProvider merges your config
509
-
510
- At render: resolveComponent("MembersDrawer", PackageMembersDrawer)
511
-
512
- Custom passed? → use MyDrawer
513
- Not passed? → use PackageMembersDrawer (built-in default)
514
- ```
515
-
516
- Rules:
517
-
518
- | You pass | Result |
519
- |----------|--------|
520
- | No `components` prop at all | **Full default chat UI** |
521
- | `components={{ Avatar: MyAvatar }}` | Custom avatar only; **all other slots use defaults** |
522
- | `components={{ MembersDrawer: undefined }}` | Treated as not passed → **default drawer** |
523
- | `classNames={{ sidebar: "..." }}` | Your classes **merge on top of** package defaults |
524
-
525
- ### Code pattern inside the package
526
-
527
- Every wired slot follows this pattern:
528
-
529
- ```tsx
530
- const MembersDrawerSlot = resolveComponent("MembersDrawer", MembersDrawer);
531
- return <MembersDrawerSlot isOpen={...} onOpenChange={...} ... />;
532
- ```
533
-
534
- Your custom component receives the **same props** as the package default (`MembersDrawerSlotProps`, etc.). Import the prop type from the package for TypeScript.
535
-
536
- ### Wrapping instead of rebuilding
537
-
538
- Use `packageDefaultComponents` when you only need to restyle or add a wrapper:
539
-
540
- ```tsx
541
- import { packageDefaultComponents, type MembersDrawerSlotProps } from "@realtimexsco/live-chat";
542
-
543
- const MyMembersDrawer = (props: MembersDrawerSlotProps) => (
544
- <div className="my-drawer-shell">
545
- <packageDefaultComponents.MembersDrawer! {...props} />
546
- </div>
547
- );
548
- ```
549
-
550
- ---
551
-
552
- ## Which slots are wired
553
-
554
- All slots below are **honored at runtime** when you pass them on `ChatMain` `components` (or `Chat` `uiConfig.components`). Slots not listed as wired may still be typed for future use or require replacing a parent slot (e.g. `ChannelHeader`).
555
-
556
- | Slot | Wired in | Fallback when omitted |
557
- |------|----------|------------------------|
558
- | `ChatLayout` | `ChatLayout.tsx` | `DefaultChatLayout` |
559
- | `LoggedUserDetails` | `DefaultChatLayout` | Package default |
560
- | `ChannelList` | `DefaultChatLayout` | Package default |
561
- | `ChannelHeader` | `DefaultChatLayout` | Package default |
562
- | `MessageList` | `DefaultChatLayout` | Package default |
563
- | `MessageInput` | `DefaultChatLayout` | Package default |
564
- | `ForwardModal` | `DefaultChatLayout` | Package default |
565
- | `ChatPanelAlerts` | `DefaultChatLayout` | Package default |
566
- | `ChannelListItem` | `ChannelListItem` wrapper | `DefaultChannelListItem` |
567
- | `Avatar` | `ChatAvatar` | `DefaultChatAvatar` |
568
- | `HeaderCallActions` | `HeaderCallActions` | Built-in phone/video group |
569
- | `PhoneCallButton` | `HeaderCallActions` | Built-in button |
570
- | `VideoCallButton` | `HeaderCallActions` | Built-in button |
571
- | `HeaderLeftSide` | `ChannelHeaderView` | Package default |
572
- | `HeaderRightSide` | `ChannelHeaderView` | Package default |
573
- | `ChatActionModal` | `ChannelHeaderView` | Package default |
574
- | `ChannelDetailsDrawer` | `ChannelHeaderView` | Package default |
575
- | `MembersDrawer` | `ChannelHeaderView` | Package default |
576
- | `AdminsDrawer` | `ChannelHeaderView` | Package default |
577
- | `PermissionDrawer` | `ChannelHeaderView` | Package default |
578
- | `AttachmentsDrawer` | `ChannelHeaderView` | Package default |
579
- | `PinnedMessagesDrawer` | `ChannelHeaderView` | Package default |
580
- | `StarredMessagesDrawer` | `ChannelHeaderView` | Package default |
581
-
582
- > **Replace entire header:** Pass `components.ChannelHeader` with your own component (use `useChannelHeader` hook for state/handlers). That replaces the full header **and** all drawers inside it.
583
-
584
- ### Advanced slots (replace parent component)
585
-
586
- These slots are **typed and exported** but used inside inner components. To customize them, either replace the parent slot or copy the parent and wire your component:
587
-
588
- | Slot | Customize by replacing |
589
- |------|------------------------|
590
- | `MessageItem` | `MessageList` (pass your list that renders custom items) |
591
- | `MessageToolbar`, `MessageStatus`, `MessageAttachments`, `MessageReactions`, `MessageReplySnippet` | `MessageItem` or `MessageList` |
592
- | `HeaderSearchOverlay` | `HeaderRightSide` or `ChannelHeader` |
593
- | `ChatThemeToggle`, `ChatDateTimeSettings`, `ChatSocketStatus` | Used inside header/sidebar — replace parent or `ChannelHeader` |
594
-
595
- For most apps, `components` + `classNames` on the [wired slots](#which-slots-are-wired) table is enough without touching advanced slots.
596
-
597
- ---
598
-
599
- ## Production integration pattern (admin templates)
600
-
601
- Recommended structure for apps like **Elstar**, **Metronic**, or any admin shell:
602
-
603
- ```
604
- src/
605
- ├── configs/
606
- │ ├── chat.config.ts # credentials only
607
- │ ├── chat.customization.config.ts # components + classNames
608
- │ └── chat.layout.config.ts # layout section toggles
609
- ├── components/chat/custom/
610
- │ ├── ElstarMembersDrawer.tsx # custom drawer example
611
- │ ├── ElstarChatAvatar.tsx
612
- │ └── index.ts
613
- └── views/Chat.tsx # wires ChatMain
614
- ```
615
-
616
- ### `chat.config.ts` — credentials only
617
-
618
- ```ts
619
- export const chatConfig = {
620
- clientId: "YOUR_CLIENT_ID",
621
- accessToken: "YOUR_JWT",
622
- loggedUserDetails: { _id: "...", name: "...", email: "..." },
623
- apiUrl: "https://api.example.com/api/v1",
624
- socketUrl: "https://api.example.com",
625
- themeColor: "#6366f1",
626
- } as const;
627
- ```
628
-
629
- ### `chat.customization.config.ts` — UI overrides
630
-
631
- ```ts
632
- import type { ChatClassNames, ChatComponents } from "@realtimexsco/live-chat";
633
- import {
634
- ElstarChatAvatar,
635
- ElstarHeaderCallActions,
636
- ElstarMembersDrawer,
637
- ElstarPhoneCallButton,
638
- ElstarVideoCallButton,
639
- } from "@/components/chat/custom";
640
-
641
- /** Only register slots you want to customize. All others use package defaults. */
642
- export const chatComponents: ChatComponents = {
643
- Avatar: ElstarChatAvatar,
644
- HeaderCallActions: ElstarHeaderCallActions,
645
- PhoneCallButton: ElstarPhoneCallButton,
646
- VideoCallButton: ElstarVideoCallButton,
647
- MembersDrawer: ElstarMembersDrawer,
648
- };
649
-
650
- export const chatClassNames: ChatClassNames = {
651
- shell: "chat-container-root",
652
- channelListItemActive: "bg-indigo-50 ring-1 ring-indigo-200",
653
- messageBubbleSender: "rounded-2xl shadow-sm",
654
- drawer: "z-[60]",
655
- drawerContent: "rounded-l-2xl",
656
- };
657
- ```
658
-
659
- ### `views/Chat.tsx` — single entry
660
-
661
- ```tsx
662
- import ChatMain, { type ChatColorMode } from "@realtimexsco/live-chat";
663
- import { chatConfig } from "@/configs/chat.config";
664
- import { chatComponents, chatClassNames } from "@/configs/chat.customization.config";
665
- import { chatLayoutConfig } from "@/configs/chat.layout.config";
666
-
667
- export default function ChatPage() {
668
- return (
669
- <div className="h-dvh min-h-0 overflow-hidden">
670
- <ChatMain
671
- {...chatConfig}
672
- layout={chatLayoutConfig}
673
- components={chatComponents}
674
- classNames={chatClassNames}
675
- showColorModeToggle={false}
676
- colorMode="light" // sync with your app theme
677
- />
678
- </div>
679
- );
680
- }
681
- ```
682
-
683
- ### Full custom `MembersDrawer` example
684
-
685
- Use the package store inside your drawer — same data/actions as the default drawer:
686
-
687
- ```tsx
688
- import { useState, useMemo } from "react";
689
- import {
690
- useChatStore,
691
- type MembersDrawerSlotProps,
692
- } from "@realtimexsco/live-chat";
693
- import Drawer from "@/components/ui/Drawer"; // your design system
694
-
695
- const MyMembersDrawer = ({
696
- isOpen,
697
- onOpenChange,
698
- conversationId,
699
- conversationInfo,
700
- }: MembersDrawerSlotProps) => {
701
- const allUsers = useChatStore((s) => s.allUsers);
702
- const manageGroupMembers = useChatStore((s) => s.manageGroupMembers);
703
- const loggedUserDetails = useChatStore((s) => s.loggedUserDetails);
704
-
705
- const participants = conversationInfo?.participants ?? [];
706
-
707
- return (
708
- <Drawer isOpen={isOpen} onClose={() => onOpenChange(false)} title="Members">
709
- {/* Your UI — add/remove members via manageGroupMembers */}
710
- <ul>
711
- {participants.map((p) => (
712
- <li key={typeof p === "string" ? p : p._id}>{typeof p === "string" ? p : p.name}</li>
713
- ))}
714
- </ul>
715
- </Drawer>
716
- );
717
- };
718
-
719
- export default MyMembersDrawer;
720
- ```
721
-
722
- Register it: `components={{ MembersDrawer: MyMembersDrawer }}`. If you **remove** that line, the package default members drawer appears again.
723
-
724
- ---
725
-
726
- ## Customization examples (Case 1–3)
727
-
728
- ### Case 1 — Replace entire chat UI
729
-
730
- ```tsx
731
- import ChatMain, {
732
- useChatController,
733
- DefaultChatLayout,
734
- type ChatLayoutSlotProps,
735
- } from "@realtimexsco/live-chat";
736
-
737
- // Option A: wrap the default layout
738
- function MyChatShell(props: ChatLayoutSlotProps) {
739
- return (
740
- <div className="flex h-full flex-col">
741
- <header className="shrink-0 border-b px-4 py-2 font-semibold">My App Chat</header>
742
- <div className="min-h-0 flex-1">
743
- <DefaultChatLayout {...props} shellClassName="rounded-none shadow-none" />
744
- </div>
745
- </div>
746
- );
747
- }
748
-
749
- // Option B: build from scratch with useChatController()
750
- function MyFullChatUI(_props: ChatLayoutSlotProps) {
751
- const {
752
- activeChannel,
753
- conversationId,
754
- conversationMessages,
755
- handleChannelSelect,
756
- handleSendMessage,
757
- setActiveChannel,
758
- state,
759
- updateState,
760
- } = useChatController();
761
-
762
- return (
763
- <div className="flex h-full">
764
- {/* Your sidebar, header, messages, input — full control */}
43
+ <ChatMain {...chatConfig} viewportHeight="screen" />
765
44
  </div>
766
45
  );
767
46
  }
768
-
769
- <ChatMain
770
- {...chatConfig}
771
- clientId={chatConfig.clientId}
772
- components={{ ChatLayout: MyChatShell }}
773
- />;
774
- ```
775
-
776
- ### Case 2 — Replace individual components
777
-
778
- ```tsx
779
- <ChatMain
780
- {...chatConfig}
781
- clientId={chatConfig.clientId}
782
- components={{
783
- Avatar: MyAvatar,
784
- ChannelListItem: MyConversationRow,
785
- MessageItem: MyMessageBubble,
786
- MessageInput: MyComposer,
787
- PhoneCallButton: MyPhoneBtn,
788
- MembersDrawer: MyMembersPanel,
789
- }}
790
- />
791
- ```
792
-
793
- ### Case 3 — Style with classNames only
794
-
795
- ```tsx
796
- <ChatMain
797
- {...chatConfig}
798
- clientId={chatConfig.clientId}
799
- classNames={{
800
- channelListItemActive: "bg-indigo-50 ring-1 ring-indigo-200",
801
- messageBubbleSender: "rounded-2xl shadow-sm",
802
- messageBubbleReceiver: "rounded-2xl bg-white",
803
- messageInput: "border-t border-gray-200",
804
- }}
805
- />
806
47
  ```
807
48
 
808
49
  ---
809
50
 
810
- ## Phone & video calling
811
-
812
- Call buttons appear in **DM chats only** (hidden for groups).
813
-
814
- ### Option A — Keep default buttons, add callbacks
815
-
816
- ```tsx
817
- features={{
818
- showPhoneCall: true,
819
- showVideoCall: true,
820
- onPhoneCall: ({ activeChannel, conversationId }) => {
821
- yourVoiceSDK.start(activeChannel.id, conversationId);
822
- },
823
- onVideoCall: ({ activeChannel, conversationId }) => {
824
- yourVideoSDK.start(activeChannel.id, conversationId);
825
- },
826
- }}
827
- ```
828
-
829
- ### Option B — Replace the call button group
830
-
831
- ```tsx
832
- components={{
833
- HeaderCallActions: ({ onPhoneCall, onVideoCall, activeChannel }) => (
834
- <div className="flex gap-2">
835
- <button type="button" onClick={onPhoneCall}>Call {activeChannel.name}</button>
836
- <button type="button" onClick={onVideoCall}>Video</button>
837
- </div>
838
- ),
839
- }}
840
- ```
841
-
842
- > Do **not** import `HeaderCallActions` from the package inside your custom `HeaderCallActions` — that causes infinite recursion. Render your own buttons instead.
843
-
844
- ### Option C — Replace individual buttons
845
-
846
- ```tsx
847
- components={{
848
- PhoneCallButton: ({ onPhoneCall, className }) => (
849
- <button type="button" className={className} onClick={onPhoneCall}>📞</button>
850
- ),
851
- VideoCallButton: ({ onVideoCall, className }) => (
852
- <button type="button" className={className} onClick={onVideoCall}>📹</button>
853
- ),
854
- }}
855
- ```
856
-
857
- ### Hide calls
51
+ ## Features
858
52
 
859
- ```tsx
860
- features={{ showPhoneCall: false, showVideoCall: false }}
861
- ```
53
+ DMs · groups · Socket.IO · reactions · replies · forward · edit/delete · pin/star · attachments · search · typing · read receipts · light/dark · locale/timezone · full UI customization
862
54
 
863
55
  ---
864
56
 
865
- ## Theme, light/dark, date/time
866
-
867
- ### Brand color
868
-
869
- ```tsx
870
- <ChatMain {...chatConfig} clientId={chatConfig.clientId} themeColor="#7c3aed" />
871
- ```
872
-
873
- Runtime updates `--chat-theme` CSS variables automatically.
874
-
875
- ### Light / dark mode
876
-
877
- | Pattern | Props |
878
- |---------|-------|
879
- | Your app controls theme (e.g. admin template) | `colorMode={mode}` `onColorModeChange={setMode}` `showColorModeToggle={false}` |
880
- | Chat manages its own theme | `defaultColorMode="light"` (built-in toggle shown) |
881
- | Start dark | `defaultColorMode="dark"` |
882
-
883
- ### Date / time
884
-
885
- ```tsx
886
- <ChatMain
887
- {...chatConfig}
888
- clientId={chatConfig.clientId}
889
- locale="en-GB"
890
- timeZone="Europe/London"
891
- hour12={false}
892
- showDateTimeSettings={false}
893
- onDateTimePreferencesChange={(prefs) => console.log(prefs)}
894
- />
895
- ```
896
-
897
- ---
898
-
899
- ## Layout sections (`layout` prop)
900
-
901
- Toggle parts of the default UI without writing new components.
902
-
903
- ```ts
904
- import { defaultChatLayoutConfig } from "@realtimexsco/live-chat";
905
-
906
- export const chatLayoutConfig = {
907
- ...defaultChatLayoutConfig,
908
- // sidebar: true, // left panel wrapper
909
- // loggedUserDetails: true, // profile + new chat button
910
- // channelList: true, // conversation list
911
- // header: true, // channel header bar
912
- // messageList: true, // message bubbles
913
- // input: true, // composer
914
- // forwardModal: true, // forward dialog
915
- };
916
- ```
917
-
918
- Examples:
919
-
920
- ```tsx
921
- layout={{ input: false }} // read-only (no composer)
922
- layout={{ sidebar: false }} // hide sidebar — messages only
923
- layout={{ forwardModal: false }} // disable forward modal
924
- layout={{ loggedUserDetails: false, channelList: true }} // list without profile block
925
- ```
926
-
927
- ---
928
-
929
- ## All component slots + examples
930
-
931
- Pass only the slots you want to override via `components` on `ChatMain` (or `uiConfig` on `Chat`).
932
-
933
- Import prop types from the package for TypeScript, e.g. `AvatarSlotProps`, `MessageItemSlotProps`.
934
-
935
- | Slot | Replaces | Prop type |
936
- |------|----------|-----------|
937
- | `ChatLayout` | Entire chat shell | `ChatLayoutSlotProps` |
938
- | `LoggedUserDetails` | Sidebar profile + new chat | `LoggedUserDetailsSlotProps` |
939
- | `ChannelList` | Conversation list | `ChannelListSlotProps` |
940
- | `ChannelListItem` | One conversation row | `ChannelListItemSlotProps` |
941
- | `ChannelHeader` | Full header bar | `ChannelHeaderSlotProps` |
942
- | `HeaderLeftSide` | Back, name, pin/star | `HeaderLeftSideSlotProps` |
943
- | `HeaderRightSide` | Search, menu, settings | `HeaderRightSideSlotProps` |
944
- | `HeaderCallActions` | Phone + video group | `HeaderCallActionsSlotProps` |
945
- | `PhoneCallButton` | Voice call button | `PhoneCallButtonSlotProps` |
946
- | `VideoCallButton` | Video call button | `VideoCallButtonSlotProps` |
947
- | `HeaderSearchOverlay` | Message search dropdown | `HeaderSearchOverlaySlotProps` |
948
- | `MessageList` | Scrollable messages area | `ActiveChannelMessagesSlotProps` |
949
- | `MessageItem` | Single message bubble | `MessageItemSlotProps` |
950
- | `MessageInput` | Composer | `ChannelMessageBoxSlotProps` |
951
- | `ForwardModal` | Forward dialog | `ForwardModalSlotProps` |
952
- | `ChatPanelAlerts` | Error/info banner | `ChatPanelAlertsSlotProps` |
953
- | `ChatActionModal` | Clear/delete/block confirm | `ChatActionModalSlotProps` |
954
- | `ChannelDetailsDrawer` | Channel details sheet | `ChannelDetailsDrawerSlotProps` |
955
- | `MembersDrawer` | Group members sheet | `MembersDrawerSlotProps` |
956
- | `AdminsDrawer` | Group admins sheet | `AdminsDrawerSlotProps` |
957
- | `PermissionDrawer` | Permissions sheet | `PermissionDrawerSlotProps` |
958
- | `AttachmentsDrawer` | Attachments sheet | `AttachmentsDrawerSlotProps` |
959
- | `PinnedMessagesDrawer` | Pinned messages sheet | `PinnedMessagesDrawerSlotProps` |
960
- | `StarredMessagesDrawer` | Starred messages sheet | `StarredMessagesDrawerSlotProps` |
961
- | `Avatar` | Avatar everywhere | `AvatarSlotProps` |
962
- | `MessageToolbar` | Reply/forward/edit menu | `MessageToolbarSlotProps` |
963
- | `MessageStatus` | Sent/delivered/read ticks | `MessageStatusSlotProps` |
964
- | `MessageAttachments` | Files/images in bubble | `MessageAttachmentsSlotProps` |
965
- | `MessageReactions` | Emoji chips | `MessageReactionsSlotProps` |
966
- | `MessageReplySnippet` | Quoted reply in bubble | `MessageReplySnippetSlotProps` |
967
- | `ChatThemeToggle` | Light/dark toggle | `{ className?: string }` |
968
- | `ChatDateTimeSettings` | Date/time settings button | `Record<string, never>` |
969
- | `ChatSocketStatus` | Connection indicator | `{ className?: string }` |
970
-
971
- ### Code examples for common slots
972
-
973
- **MembersDrawer** (full custom drawer — Elstar-style)
974
-
975
- ```tsx
976
- import { useChatStore, type MembersDrawerSlotProps } from "@realtimexsco/live-chat";
977
-
978
- const MyMembersDrawer = ({
979
- isOpen,
980
- onOpenChange,
981
- conversationId,
982
- conversationInfo,
983
- }: MembersDrawerSlotProps) => {
984
- const manageGroupMembers = useChatStore((s) => s.manageGroupMembers);
985
- const participants = conversationInfo?.participants ?? [];
986
-
987
- if (!isOpen) return null;
988
-
989
- return (
990
- <aside className="members-drawer">
991
- <header>
992
- <h2>Members ({participants.length})</h2>
993
- <button type="button" onClick={() => onOpenChange(false)}>Close</button>
994
- </header>
995
- <ul>
996
- {participants.map((p, i) => {
997
- const id = typeof p === "string" ? p : p._id;
998
- const name = typeof p === "string" ? "User" : p.name;
999
- return <li key={`${id}-${i}`}>{name}</li>;
1000
- })}
1001
- </ul>
1002
- </aside>
1003
- );
1004
- };
1005
- ```
1006
-
1007
- **ChannelDetailsDrawer**
1008
-
1009
- ```tsx
1010
- import type { ChannelDetailsDrawerSlotProps } from "@realtimexsco/live-chat";
1011
-
1012
- const MyDetailsDrawer = ({ isOpen, onOpenChange, activeChannel, conversationInfo }: ChannelDetailsDrawerSlotProps) => (
1013
- <dialog open={isOpen} onClose={() => onOpenChange(false)}>
1014
- <h2>{activeChannel.name}</h2>
1015
- <p>{conversationInfo?.groupDescription ?? "Direct message"}</p>
1016
- </dialog>
1017
- );
1018
- ```
1019
-
1020
- **Avatar**
1021
-
1022
- ```tsx
1023
- import type { AvatarSlotProps } from "@realtimexsco/live-chat";
1024
-
1025
- const MyAvatar = ({ src, name, size = "md", isOnline, showOnline }: AvatarSlotProps) => (
1026
- <div className="relative">
1027
- <img src={src || undefined} alt={name} className={`avatar avatar--${size}`} />
1028
- {showOnline && isOnline && <span className="online-dot" />}
1029
- </div>
1030
- );
1031
- ```
1032
-
1033
- **MessageItem** (custom bubble)
1034
-
1035
- ```tsx
1036
- import type { MessageItemSlotProps } from "@realtimexsco/live-chat";
1037
-
1038
- const MyMessageItem = ({ isSender, message, createdAt }: MessageItemSlotProps) => (
1039
- <div className={isSender ? "text-right" : "text-left"}>
1040
- <div className={isSender ? "bubble-sent" : "bubble-received"}>{message}</div>
1041
- <time className="text-xs opacity-60">{createdAt}</time>
1042
- </div>
1043
- );
1044
- ```
1045
-
1046
- **ChannelListItem**
1047
-
1048
- ```tsx
1049
- import type { ChannelListItemSlotProps } from "@realtimexsco/live-chat";
1050
-
1051
- const MyRow = ({ channel, isActive, onSelect }: ChannelListItemSlotProps) => (
1052
- <button
1053
- type="button"
1054
- className={isActive ? "row-active" : "row"}
1055
- onClick={() => onSelect(channel)}
1056
- >
1057
- {channel.name}
1058
- {channel.unreadCount ? ` (${channel.unreadCount})` : ""}
1059
- </button>
1060
- );
1061
- ```
1062
-
1063
- **MessageInput**
1064
-
1065
- ```tsx
1066
- import type { ChannelMessageBoxSlotProps } from "@realtimexsco/live-chat";
1067
-
1068
- const MyInput = ({ onSendMessage, replyTo, onCancelReply }: ChannelMessageBoxSlotProps) => {
1069
- const [text, setText] = useState("");
1070
- return (
1071
- <div>
1072
- {replyTo && <button type="button" onClick={onCancelReply}>Cancel reply</button>}
1073
- <input value={text} onChange={(e) => setText(e.target.value)} />
1074
- <button type="button" onClick={() => { onSendMessage?.(text, [], replyTo); setText(""); }}>
1075
- Send
1076
- </button>
1077
- </div>
1078
- );
1079
- };
1080
- ```
1081
-
1082
- ---
1083
-
1084
- ## All classNames keys
1085
-
1086
- Merged with package defaults — your classes extend styling without replacing components.
1087
-
1088
- | Key | UI region |
1089
- |-----|-----------|
1090
- | `shell` | Outermost chat wrapper |
1091
- | `mainPanel` | Inner rounded panel |
1092
- | `sidebar` | Left sidebar |
1093
- | `loggedUserDetails` | Profile block at top |
1094
- | `channelList` | Conversation list container |
1095
- | `channelListItem` | Each conversation row |
1096
- | `channelListItemActive` | Selected conversation row |
1097
- | `channelHeader` | Header bar |
1098
- | `headerLeft` | Header left section |
1099
- | `headerRight` | Header right section |
1100
- | `headerCallActions` | Call buttons wrapper |
1101
- | `phoneCallButton` | Phone button |
1102
- | `videoCallButton` | Video button |
1103
- | `messageList` | Messages scroll area |
1104
- | `messageItem` | Message row wrapper |
1105
- | `messageBubble` | Bubble (both sides) |
1106
- | `messageBubbleSender` | Outgoing bubble |
1107
- | `messageBubbleReceiver` | Incoming bubble |
1108
- | `messageInput` | Composer container |
1109
- | `messageInputTextarea` | Textarea element |
1110
- | `avatar` | Avatar wrapper |
1111
- | `avatarFallback` | Initials fallback |
1112
- | `dateDivider` | "Today" / date separator |
1113
- | `emptyState` | No messages placeholder |
1114
- | `forwardModal` | Forward dialog |
1115
- | `chatPanelAlerts` | Error/info alert bar |
1116
- | `drawer` | Drawer overlay/shell |
1117
- | `drawerContent` | Drawer panel body |
1118
- | `searchOverlay` | In-header search results |
1119
-
1120
- ```tsx
1121
- classNames={{
1122
- shell: "chat-container-root",
1123
- channelListItemActive: "bg-indigo-50 dark:bg-indigo-500/10",
1124
- messageBubbleSender: "rounded-2xl shadow-sm",
1125
- messageBubbleReceiver: "rounded-2xl",
1126
- drawer: "z-[60]",
1127
- }}
1128
- ```
1129
-
1130
- ---
1131
-
1132
- ## Wrapping package defaults (`packageDefaultComponents`)
1133
-
1134
- The package exports every built-in slot component so you can wrap defaults with your theme instead of rebuilding from scratch.
1135
-
1136
- ```tsx
1137
- import {
1138
- packageDefaultComponents,
1139
- type ChatComponents,
1140
- } from "@realtimexsco/live-chat";
1141
-
1142
- function withTheme<P extends object>(
1143
- Default: React.ComponentType<P>,
1144
- className: string
1145
- ): React.ComponentType<P> {
1146
- return (props) => (
1147
- <div className={className}>
1148
- <Default {...props} />
1149
- </div>
1150
- );
1151
- }
1152
-
1153
- const components: ChatComponents = {
1154
- MessageItem: withTheme(packageDefaultComponents.MessageItem!, "my-message"),
1155
- ChannelListItem: withTheme(packageDefaultComponents.ChannelListItem!, "my-row"),
1156
- Avatar: MyFullyCustomAvatar, // or use DefaultChatAvatar from package
1157
- };
1158
- ```
1159
-
1160
- Also exported for reference:
1161
-
1162
- - `packageDefaultComponents` — map of all slot defaults
1163
- - `DefaultChatAvatar` — inner avatar (use when customizing `Avatar` slot)
1164
- - `DefaultChannelListItem` — inner list row (use when customizing `ChannelListItem` slot)
1165
-
1166
- ---
1167
-
1168
- ## API query params
1169
-
1170
- Pass extra query string params from your app into package HTTP requests. Params are merged in the axios interceptor (built-in params like `limit` / `page` / `conversationId` still win if they conflict).
1171
-
1172
- **Default behavior:** `queryParams` apply to **all GET APIs** only (`queryParamApis` defaults to `['get']`). Login, create group, and other POST/PATCH calls are **not** affected unless you opt in with `queryParamApis={['all']}`.
1173
-
1174
- ---
1175
-
1176
- ### 1. Default — same params on every GET API
1177
-
1178
- **When to use:** Your app needs the same filter (e.g. `branchId`) on every list/fetch GET call inside the chat package.
1179
-
1180
- **Explanation:** You only pass `queryParams`. You do **not** need `queryParamApis` — it defaults to `['get']`, so every GET endpoint below receives these params. POST/PATCH stay clean.
1181
-
1182
- ```tsx
1183
- <ChatMain
1184
- {...chatConfig}
1185
- queryParams={{ branchId: "12" }}
1186
- // queryParamApis default = ['get']
1187
- />
1188
- ```
1189
-
1190
- **What happens on the network:**
1191
-
1192
- | Request | Query string |
1193
- |---------|----------------|
1194
- | `GET /user/list` | `?branchId=12` |
1195
- | `GET /conversation/list` | `?limit=20&page=1&branchId=12` |
1196
- | `GET /message/get-all-from-conversation` | `?conversationId=…&limit=…&branchId=12` |
1197
- | `GET …/pinned-messages` | `?conversationId=…&branchId=12` |
1198
- | `POST /user/login/client-user` | *(no extra query params)* |
1199
-
1200
- **GET APIs covered by default (`CHAT_GET_API_KEYS`):**
1201
-
1202
- | Key | Endpoint |
1203
- |-----|----------|
1204
- | `users` | `GET /user/list` |
1205
- | `conversations` | `GET /conversation/list` |
1206
- | `conversationInfo` | `GET /conversation/get/information/:id` |
1207
- | `messages` | `GET /message/get-all-from-conversation` |
1208
- | `pinnedMessages` | `GET message/pinned-messages` |
1209
- | `starredMessages` | `GET message/starred-messages` |
1210
- | `searchMessages` | `GET /message/search-messages` |
1211
- | `searchMessagesContext` | `GET /message/searched-message/context` |
1212
-
1213
- ```tsx
1214
- import { CHAT_GET_API_KEYS, CHAT_API_KEYS } from "@realtimexsco/live-chat";
1215
- ```
1216
-
1217
- ---
1218
-
1219
- ### 2. Different params per GET API
1220
-
1221
- **When to use:** Each endpoint needs its own query keys (e.g. conversations need `branchId`, messages need `locale`, users need `orgId`).
1222
-
1223
- **Explanation:** Use `queryParamsByApi` with named API keys. Only the APIs you list get params — and each can have a different object. You do not need `queryParams` or `queryParamApis` for this pattern.
1224
-
1225
- ```tsx
1226
- <ChatMain
1227
- {...chatConfig}
1228
- queryParamsByApi={{
1229
- conversations: { branchId: "12" },
1230
- messages: { locale: "en" },
1231
- users: { orgId: "x" },
1232
- }}
1233
- />
1234
- ```
1235
-
1236
- **What happens on the network:**
1237
-
1238
- | Request | Query string |
1239
- |---------|----------------|
1240
- | `GET /conversation/list` | `?…&branchId=12` |
1241
- | `GET /message/get-all-from-conversation` | `?…&locale=en` |
1242
- | `GET /user/list` | `?orgId=x` |
1243
- | Other GET APIs (not listed) | *(no extra params from this prop)* |
1244
-
1245
- ---
1246
-
1247
- ### 3. Only one GET API
1248
-
1249
- **When to use:** You want shared-style `queryParams`, but only on a single endpoint (e.g. conversation list), not on messages or users.
1250
-
1251
- **Explanation:** Pass `queryParams` for the values, and restrict with `queryParamApis={['conversations']}`. Other GET APIs will not receive `branchId`.
1252
-
1253
- ```tsx
1254
- <ChatMain
1255
- {...chatConfig}
1256
- queryParams={{ branchId: "12" }}
1257
- queryParamApis={["conversations"]}
1258
- />
1259
- ```
1260
-
1261
- **What happens on the network:**
1262
-
1263
- | Request | Query string |
1264
- |---------|----------------|
1265
- | `GET /conversation/list` | `?limit=20&page=1&branchId=12` |
1266
- | `GET /message/get-all-from-conversation` | *(no `branchId`)* |
1267
- | `GET /user/list` | *(no `branchId`)* |
1268
-
1269
- You can also target multiple selected GETs:
1270
-
1271
- ```tsx
1272
- <ChatMain
1273
- {...chatConfig}
1274
- queryParams={{ orgId: "x" }}
1275
- queryParamApis={["messages", "users"]}
1276
- />
1277
- ```
1278
-
1279
- ---
1280
-
1281
- ### Bonus: shared + per-API together
1282
-
1283
- **Explanation:** Shared params apply to the APIs in `queryParamApis`. Per-API params from `queryParamsByApi` merge on top for that endpoint.
1284
-
1285
- ```tsx
1286
- <ChatMain
1287
- {...chatConfig}
1288
- queryParams={{ tenant: "abc" }}
1289
- queryParamApis={["conversations", "messages"]}
1290
- queryParamsByApi={{
1291
- conversations: { branchId: "12" },
1292
- messages: { locale: "en" },
1293
- }}
1294
- />
1295
- ```
1296
-
1297
- - conversations → `?tenant=abc&branchId=12`
1298
- - messages → `?tenant=abc&locale=en`
1299
-
1300
- ---
1301
-
1302
- ### `queryParamApis` options
1303
-
1304
- | Value | Meaning |
1305
- |-------|---------|
1306
- | `['get']` | **Default.** All GET endpoints in the table above |
1307
- | `['all']` | Every `apiClient` request (GET + POST + PATCH) |
1308
- | Specific keys | Only those endpoints (e.g. `['conversations']`) |
1309
-
1310
- ### Imperative helpers
1311
-
1312
- ```tsx
1313
- import {
1314
- setChatQueryParams,
1315
- setChatQueryParamApis,
1316
- setChatQueryParamsByApi,
1317
- } from "@realtimexsco/live-chat";
1318
-
1319
- setChatQueryParams({ branchId: "12" });
1320
- setChatQueryParamApis(["conversations"]);
1321
- setChatQueryParamsByApi({ messages: { locale: "en" } });
1322
- ```
1323
-
1324
- Socket connection query and S3 presigned uploads are **not** changed by these props.
1325
-
1326
- ---
1327
-
1328
- ## ChatMain props reference
1329
-
1330
- | Prop | Type | Default | Description |
1331
- |------|------|---------|-------------|
1332
- | `clientId` | `string` | — | **Required.** Tenant / client id |
1333
- | `accessToken` | `string` | — | **Required.** JWT access token |
1334
- | `loggedUserDetails` | `object` | — | **Required.** `{ _id, name, email }` — `_id` must match JWT |
1335
- | `apiUrl` | `string` | — | **Required.** REST API ending with `/api/v1` (pass from your env/config) |
1336
- | `socketUrl` | `string` | derived | Optional socket host (no `/api/v1`). Defaults to `apiUrl` without `/api/v1` |
1337
- | `queryParams` | `Record<string, string \| number \| boolean>` | — | Shared query params for selected APIs (see `queryParamApis`) |
1338
- | `queryParamApis` | `ChatApiKey[]` | `['get']` | Which APIs receive `queryParams`. Default = all GET endpoints |
1339
- | `queryParamsByApi` | `ChatQueryParamsByApi` | — | Different query params per API key |
1340
- | `viewportHeight` | `"full" \| "screen" \| string` | `"full"` | Chat root height. Use `"screen"` for full page, or `"calc(100dvh - 4rem)"` below fixed header |
1341
- | `viewportClassName` | `string` | — | Extra classes on the built-in viewport wrapper |
1342
- | `colorMode` | `"light" \| "dark"` | — | Controlled light/dark (use with your app theme) |
1343
- | `defaultColorMode` | `"light" \| "dark"` | `"light"` | Initial mode when uncontrolled |
1344
- | `onColorModeChange` | `(mode) => void` | — | Called when theme changes |
1345
- | `showColorModeToggle` | `boolean` | `true` | Show built-in light/dark toggle |
1346
- | `locale` | `string` | browser | BCP 47 locale, e.g. `"en-US"` |
1347
- | `timeZone` | `string` | browser | IANA timezone, e.g. `"Asia/Kolkata"` |
1348
- | `hour12` | `boolean` | locale | `true` = 12-hour clock |
1349
- | `defaultLocale` | `string` | — | Initial locale when uncontrolled |
1350
- | `defaultTimeZone` | `string` | — | Initial timezone when uncontrolled |
1351
- | `defaultHour12` | `boolean` | — | Initial 12/24h when uncontrolled |
1352
- | `onDateTimePreferencesChange` | `(prefs) => void` | — | User changed date/time settings |
1353
- | `showDateTimeSettings` | `boolean` | `true` | Show in-panel date/time button |
1354
- | `layout` | `ChatLayoutConfig` | all `true` | Toggle UI sections |
1355
- | `components` | `ChatComponents` | — | Replace UI components (Case 1 & 2) |
1356
- | `classNames` | `ChatClassNames` | — | Per-region CSS classes (Case 3) |
1357
- | `features` | `ChatFeatures` | — | Phone/video call flags & callbacks |
1358
- | `uiConfig` | `UIConfig` | — | Advanced: merged with `components` / `classNames` / `features` |
1359
- | `error` | `string \| null` | — | Error message in alert bar |
1360
- | `info` | `string \| null` | — | Info message in alert bar |
1361
- | `onErrorDismiss` | `() => void` | — | Dismiss error |
1362
- | `onInfoDismiss` | `() => void` | — | Dismiss info |
1363
-
1364
- ---
1365
-
1366
- ## Chat provider (composable mode)
1367
-
1368
- Use `Chat` + `ChatLayout` when you need a custom page wrapper but not a full `ChatMain` replacement.
1369
-
1370
- ```tsx
1371
- "use client";
1372
-
1373
- import { Chat, ChatLayout, type ChatComponents } from "@realtimexsco/live-chat";
1374
- import { chatConfig } from "@/configs/chat.config";
1375
-
1376
- const components: ChatComponents = {
1377
- Avatar: MyAvatar,
1378
- };
1379
-
1380
- export default function ChatPage() {
1381
- return (
1382
- <div className="h-dvh min-h-0 overflow-hidden">
1383
- <Chat
1384
- client={{ id: chatConfig.clientId }}
1385
- accessToken={chatConfig.accessToken}
1386
- loggedUserDetails={chatConfig.loggedUserDetails}
1387
- apiUrl={chatConfig.apiUrl}
1388
- socketUrl={chatConfig.socketUrl}
1389
- themeColor={chatConfig.themeColor}
1390
- uiConfig={{
1391
- components,
1392
- classNames: { sidebar: "bg-slate-900" },
1393
- features: {
1394
- onPhoneCall: (ctx) => console.log("call", ctx),
1395
- },
1396
- }}
1397
- >
1398
- <ChatLayout themeColor={chatConfig.themeColor} />
1399
- </Chat>
1400
- </div>
1401
- );
1402
- }
1403
- ```
1404
-
1405
- ### `Chat` provider props
1406
-
1407
- | Prop | Type | Required | Description |
1408
- |------|------|----------|-------------|
1409
- | `client` | `{ id: string }` | Yes | Client id |
1410
- | `accessToken` | `string` | Yes | JWT |
1411
- | `loggedUserDetails` | `object` | Yes | User profile |
1412
- | `apiUrl` | `string` | No | REST base URL |
1413
- | `socketUrl` | `string` | No | Socket host |
1414
- | `queryParams` | `ChatQueryParams` | No | Shared query params (default target: all GET APIs) |
1415
- | `queryParamApis` | `ChatApiKey[]` | No | Which APIs receive `queryParams` (default `['get']`) |
1416
- | `queryParamsByApi` | `ChatQueryParamsByApi` | No | Per-API query params |
1417
- | `themeColor` | `string` | No | Accent color |
1418
- | `uiConfig` | `UIConfig` | No | `components`, `classNames`, `features` |
1419
- | `onMessageReceived` | `(msg) => void` | No | Background message callback |
1420
- | `onSocketConnected` | `(id) => void` | No | Socket connected |
1421
- | `onSocketError` | `(err) => void` | No | Socket error |
1422
- | `children` | `ReactNode` | Yes | Usually `<ChatLayout />` |
1423
-
1424
- ---
1425
-
1426
- ## Hooks & store
1427
-
1428
- | Hook / export | Purpose |
1429
- |---------------|---------|
1430
- | `useChatController()` | State + handlers inside custom `ChatLayout` (Case 1) |
1431
- | `useChatStore()` | Zustand store — messages, conversations, socket actions |
1432
- | `useChatCustomization()` | Current `components` / `classNames` inside custom children |
1433
- | `useChatFeatures()` | Phone/video flags and callbacks |
1434
- | `useChatTheme()` | `themeColor`, `isDark`, CSS variables |
1435
- | `useChatLocale()` | Format dates/times with current locale |
1436
- | `useChatAlerts()` | Error/info alert state |
1437
- | `useChatContext()` | Low-level chat provider context |
1438
-
1439
- ```tsx
1440
- import { useChatStore } from "@realtimexsco/live-chat";
1441
-
1442
- const conversations = useChatStore((s) => s.conversations);
1443
- const emitDmSend = useChatStore((s) => s.emitDmSend);
1444
- ```
1445
-
1446
- ---
1447
-
1448
- ## React (Vite) — full setup
1449
-
1450
- ### 1. Create project
1451
-
1452
- ```bash
1453
- pnpm create vite my-chat-app --template react-ts
1454
- cd my-chat-app
1455
- ```
1456
-
1457
- ### 2. Install
1458
-
1459
- ```bash
1460
- pnpm add @realtimexsco/live-chat react react-dom zustand tailwindcss
1461
- pnpm add -D @tailwindcss/vite @vitejs/plugin-react
1462
- ```
1463
-
1464
- ### 3. File structure
1465
-
1466
- ```
1467
- src/
1468
- ├── main.tsx
1469
- ├── App.tsx
1470
- ├── index.css ← Tailwind + package CSS (see above)
1471
- └── configs/
1472
- └── chat.config.ts
1473
- ```
1474
-
1475
- ### 4. `src/main.tsx`
1476
-
1477
- ```tsx
1478
- import { createRoot } from "react-dom/client";
1479
- import "./index.css";
1480
- import App from "./App";
1481
-
1482
- createRoot(document.getElementById("root")!).render(<App />);
1483
- ```
1484
-
1485
- ### 5. `src/App.tsx`
1486
-
1487
- ```tsx
1488
- import ChatMain from "@realtimexsco/live-chat";
1489
- import { chatConfig } from "./configs/chat.config";
1490
-
1491
- export default function App() {
1492
- return (
1493
- <div className="h-dvh min-h-0 overflow-hidden">
1494
- <ChatMain
1495
- clientId={chatConfig.clientId}
1496
- accessToken={chatConfig.accessToken}
1497
- loggedUserDetails={chatConfig.loggedUserDetails}
1498
- apiUrl={chatConfig.apiUrl}
1499
- socketUrl={chatConfig.socketUrl}
1500
- themeColor={chatConfig.themeColor}
1501
- />
1502
- </div>
1503
- );
1504
- }
1505
- ```
1506
-
1507
- ### 6. Run
1508
-
1509
- ```bash
1510
- pnpm dev
1511
- ```
1512
-
1513
- ---
1514
-
1515
- ## Next.js — full setup
1516
-
1517
- ### 1. Create project
1518
-
1519
- ```bash
1520
- pnpm create next-app@latest my-chat-app --typescript --tailwind --app --src-dir
1521
- cd my-chat-app
1522
- ```
1523
-
1524
- ### 2. Install
1525
-
1526
- ```bash
1527
- pnpm add @realtimexsco/live-chat zustand
1528
- pnpm add -D @tailwindcss/postcss
1529
- ```
1530
-
1531
- ### 3. `src/app/chat/page.tsx`
1532
-
1533
- ```tsx
1534
- "use client";
1535
-
1536
- import ChatMain from "@realtimexsco/live-chat";
1537
- import { chatConfig } from "@/configs/chat.config";
1538
-
1539
- export default function ChatPage() {
1540
- return (
1541
- <div className="h-dvh min-h-0 overflow-hidden">
1542
- <ChatMain
1543
- clientId={chatConfig.clientId}
1544
- accessToken={chatConfig.accessToken}
1545
- loggedUserDetails={chatConfig.loggedUserDetails}
1546
- apiUrl={chatConfig.apiUrl}
1547
- socketUrl={chatConfig.socketUrl}
1548
- themeColor={chatConfig.themeColor}
1549
- />
1550
- </div>
1551
- );
1552
- }
1553
- ```
1554
-
1555
- ### 4. Run
1556
-
1557
- ```bash
1558
- pnpm dev
1559
- ```
1560
-
1561
- Open `http://localhost:3000/chat`
1562
-
1563
- ---
1564
-
1565
- ## Environment variables
1566
-
1567
- Optional — use in `chat.config.ts`:
1568
-
1569
- ```env
1570
- # .env.local (Next.js) or .env (Vite with VITE_ prefix)
1571
- NEXT_PUBLIC_CHAT_CLIENT_ID=your-client-id
1572
- NEXT_PUBLIC_API_V1_BASE_URL=https://realtimex.softwareco.com/api/v1
1573
- NEXT_PUBLIC_SOCKET_URL=https://realtimex.softwareco.com
1574
- ```
1575
-
1576
- ```ts
1577
- export const chatConfig = {
1578
- clientId: process.env.NEXT_PUBLIC_CHAT_CLIENT_ID ?? "",
1579
- accessToken: "YOUR_JWT_ACCESS_TOKEN",
1580
- apiUrl: process.env.NEXT_PUBLIC_API_V1_BASE_URL ?? "",
1581
- socketUrl:
1582
- process.env.NEXT_PUBLIC_SOCKET_URL ??
1583
- (process.env.NEXT_PUBLIC_API_V1_BASE_URL ?? "").replace(/\/api\/v1\/?$/, ""),
1584
- // ...
1585
- };
1586
- ```
1587
-
1588
- Vite uses `import.meta.env.VITE_*` instead of `NEXT_PUBLIC_*`.
1589
-
1590
- ---
1591
-
1592
- ## Dev proxy (CORS)
1593
-
1594
- If the API blocks browser requests during local dev, proxy through your dev server.
1595
-
1596
- **Vite `vite.config.ts`:**
1597
-
1598
- ```ts
1599
- server: {
1600
- proxy: {
1601
- "/chat-api": {
1602
- target: "https://YOUR_API_HOST",
1603
- changeOrigin: true,
1604
- rewrite: (path) => path.replace(/^\/chat-api/, "/api/v1"),
1605
- },
1606
- },
1607
- },
1608
- ```
1609
-
1610
- Then set `apiUrl: "/chat-api"` in `chat.config.ts` for local development.
1611
-
1612
- ---
1613
-
1614
- ## Architecture
1615
-
1616
- ```
1617
- ChatMain
1618
- ├── ChatThemeProvider (themeColor, light/dark)
1619
- ├── ChatLocaleProvider (locale, timezone, hour12)
1620
- ├── Chat
1621
- │ ├── Auth + REST API + Socket.IO + Zustand store
1622
- │ └── ChatCustomizationProvider (components, classNames, features)
1623
- │ └── ChatLayout (resolveComponent → DefaultChatLayout or custom)
1624
- │ ├── Sidebar: LoggedUserDetails + ChannelList
1625
- │ └── Main: ChannelHeader + MessageList + MessageInput
1626
- │ └── ChannelHeaderView
1627
- │ ├── resolveComponent → drawers (Members, Details, …)
1628
- │ └── resolveComponent → modals (Clear, Delete, Block)
1629
- └── ChatAlertsProvider (error / info banner)
1630
- ```
1631
-
1632
- **Customization flow:**
1633
-
1634
- 1. You pass `components` / `classNames` / `features` on `ChatMain`.
1635
- 2. `ChatCustomizationProvider` merges your config (unset slots stay empty).
1636
- 3. Each render site calls `resolveComponent(slot, PackageDefault)` — custom wins, else default.
1637
- 4. No `components` prop → every slot uses package defaults (full default chat UI).
1638
-
1639
- **Runtime flow:**
1640
-
1641
- 1. `ChatMain` runs client-user login, sets JWT, connects socket.
1642
- 2. User picks a conversation → messages fetch + socket room join.
1643
- 3. Send is optimistic — message appears immediately, reconciles on server ack.
1644
- 4. Socket events update Zustand → UI re-renders automatically.
1645
-
1646
- ---
1647
-
1648
- ## All exports
1649
-
1650
- ```ts
1651
- import ChatMain, {
1652
- // ── Providers & layout ──
1653
- Chat,
1654
- ChatLayout,
1655
- DefaultChatLayout,
1656
- Channel,
1657
- ChatThemeProvider,
1658
- ChatLocaleProvider,
1659
- ChatAlertsProvider,
1660
- ChatCustomizationProvider,
1661
-
1662
- // ── UI components (standalone or customization reference) ──
1663
- ChannelList,
1664
- ChannelHeader,
1665
- MessageList,
1666
- MessageInput,
1667
- LoggedUserDetails,
1668
- ForwardModal,
1669
- MessageItem,
1670
- ChatAvatar,
1671
- DefaultChatAvatar,
1672
- ChannelListItem,
1673
- DefaultChannelListItem,
1674
- HeaderCallActions,
1675
- packageDefaultComponents,
1676
-
1677
- // ── Hooks ──
1678
- useChatStore,
1679
- useChatController,
1680
- useChatContext,
1681
- useChatCustomization,
1682
- useChatCustomizationOptional,
1683
- useChatFeatures,
1684
- useChatFeaturesOptional,
1685
- useChatTheme,
1686
- useChatLocale,
1687
- useChatAlerts,
1688
-
1689
- // ── Layout helpers ──
1690
- mergeChatLayoutConfig,
1691
- defaultChatLayoutConfig,
1692
- mergeChatCustomization,
1693
- defaultChatClassNames,
1694
- defaultChatComponents,
1695
- defaultChatFeatures,
1696
-
1697
- // ── API / socket helpers ──
1698
- setChatClientId,
1699
- setChatAccessToken,
1700
- setChatBaseURL,
1701
- setChatSocketUrl,
1702
- setChatQueryParams,
1703
- getChatQueryParams,
1704
- setChatQueryParamApis,
1705
- getChatQueryParamApis,
1706
- setChatQueryParamsByApi,
1707
- getChatQueryParamsByApi,
1708
- CHAT_API_KEYS,
1709
- CHAT_GET_API_KEYS,
1710
- resolveChatApiKey,
1711
- socketService,
1712
- getSocketConfig,
1713
- showNotification,
1714
- downloadFile,
1715
-
1716
- // ── Types ──
1717
- type ChatApiKey,
1718
- type ChatGetApiKey,
1719
- type ChatQueryParams,
1720
- type ChatQueryParamsByApi,
1721
- type ChatComponents,
1722
- type ChatClassNames,
1723
- type ChatFeatures,
1724
- type ChatCallContext,
1725
- type ChatLayoutConfig,
1726
- type ChatColorMode,
1727
- type ChatCustomizationConfig,
1728
- type UIConfig,
1729
- type IChannel,
1730
- type IMessage,
1731
- type IAttachment,
1732
- type AvatarSlotProps,
1733
- type MessageItemSlotProps,
1734
- type ChannelHeaderSlotProps,
1735
- type ChannelListItemSlotProps,
1736
- type ChatLayoutSlotProps,
1737
- type HeaderCallActionsSlotProps,
1738
- type PhoneCallButtonSlotProps,
1739
- type VideoCallButtonSlotProps,
1740
- type ActiveChannelMessagesSlotProps,
1741
- type ChannelMessageBoxSlotProps,
1742
- type ForwardModalSlotProps,
1743
- type MembersDrawerSlotProps,
1744
- // … all *SlotProps types are exported
1745
- } from "@realtimexsco/live-chat";
1746
- ```
1747
-
1748
- **CSS import (not from JS):**
1749
-
1750
- ```css
1751
- @import "@realtimexsco/live-chat/styles.css";
1752
- ```
1753
-
1754
- ---
1755
-
1756
- ## Troubleshooting
1757
-
1758
- | Problem | Cause | Fix |
1759
- |---------|-------|-----|
1760
- | Unstyled / broken UI | Missing Tailwind `@source` | Add `@source` to package `dist/index.mjs` and `dist/index.cjs` |
1761
- | No styles at all | Missing `styles.css` import | `@import "@realtimexsco/live-chat/styles.css"` |
1762
- | Wrong `@source` depth | Next.js vs Vite path | Use `../../node_modules` in `src/app/globals.css` |
1763
- | TypeScript: no declaration file | Published tarball missing `dist/index.d.ts` | Reinstall latest version; verify `dist/index.d.ts` exists |
1764
- | Next.js build error | Package not transpiled | `transpilePackages: ["@realtimexsco/live-chat"]` |
1765
- | `use client` error | Server Component import | Add `"use client"` to chat page |
1766
- | Whole page scrolls, not messages | Missing height chain | Set `viewportHeight="screen"` or `viewportHeight="calc(100dvh - 4rem)"` on `<ChatMain />` |
1767
- | Messages don't load | Wrong credentials / CORS | Check `apiUrl`, JWT, proxy in dev |
1768
- | Socket won't connect | Wrong `socketUrl` | No `/api/v1` on socket URL |
1769
- | Duplicate React hooks error | Duplicate React copies | `dedupe: ["react", "react-dom"]` in Vite |
1770
- | Sent message on **left** and **right** | `_id` mismatch or duplicate echo | Ensure `loggedUserDetails._id` matches JWT `_id` |
1771
- | Custom layout has no data | Missing hook | Call `useChatController()` inside custom `ChatLayout` |
1772
- | Custom `MembersDrawer` still shows package UI | Old package version (< 1.1.55) | Upgrade to **1.1.55+** — drawer slots now use `resolveComponent` |
1773
- | Custom drawer works but other slots are default | Expected behavior | Only pass slots you want to override in `components` |
1774
- | Passed `components` but slot unchanged | Slot not wired or parent replaced | See [Which slots are wired](#which-slots-are-wired); use `ChannelHeader` to replace all header drawers |
1775
- | Custom `HeaderCallActions` crashes | Recursive import | Don't import package `HeaderCallActions` inside your slot |
1776
- | Vite stale UI after upgrade | Cached prebundle | `rm -rf node_modules/.vite && pnpm dev` |
1777
- | Rate limit banner | API returned 429 | Wait for cooldown; reduce rapid API calls in dev |
1778
-
1779
- ---
1780
-
1781
- ## Setup checklist
1782
-
1783
- Copy this list when integrating into a new project:
1784
-
1785
- - [ ] Node.js 18+
1786
- - [ ] `pnpm add @realtimexsco/live-chat react react-dom zustand tailwindcss`
1787
- - [ ] Tailwind v4 plugin installed (`@tailwindcss/vite` or `@tailwindcss/postcss`)
1788
- - [ ] `index.css` / `globals.css` imports `tailwindcss` + `@realtimexsco/live-chat/styles.css`
1789
- - [ ] `@source` points to `dist/index.mjs` and `dist/index.cjs` (correct depth for your CSS path)
1790
- - [ ] `:root` has `--chat-theme` and `--chat-theme-5` … `--chat-theme-60`
1791
- - [ ] `@theme inline` maps `--color-chat-theme*` variables
1792
- - [ ] `chat.config.ts` with real `clientId`, `accessToken`, `apiUrl`, `socketUrl`
1793
- - [ ] `loggedUserDetails._id` matches JWT `_id`
1794
- - [ ] Chat page wrapped in `h-dvh min-h-0 overflow-hidden`
1795
- - [ ] Next.js: `"use client"` on chat page + `transpilePackages`
1796
- - [ ] Vite: `optimizeDeps.exclude` includes `@realtimexsco/live-chat`
1797
- - [ ] Optional: `components` for custom UI (Case 1 / 2)
1798
- - [ ] Optional: `classNames` for theme tweaks (Case 3)
1799
- - [ ] Optional: `features` for phone/video callbacks
1800
- - [ ] Optional: `layout` to hide sections
1801
- - [ ] Optional: `colorMode` synced with your app theme
1802
- - [ ] Verify: `node_modules/@realtimexsco/live-chat/dist/index.d.ts` exists
1803
- - [ ] Use package **≥ 1.1.56** for drawer customization + default fallback fixes
1804
-
1805
- ---
1806
-
1807
- ## Package development (maintainers)
1808
-
1809
- This package is built from the **live-chat-frontend** reference app. Source lives in `src/`; published output is `dist/`.
1810
-
1811
- ### Sync from frontend
1812
-
1813
- ```bash
1814
- cd chat-ui-package
1815
- pnpm sync # copies app/chat → src/chat, patches API client & types, fixes imports
1816
- pnpm build # tsup → dist/
1817
- ```
1818
-
1819
- `scripts/sync-from-frontend.mjs` copies UI from the frontend repo. Protected files (not overwritten):
1820
-
1821
- - `src/index.ts`, `ChatMain.tsx`, `ChatContext.tsx`, `DefaultChatLayout.tsx`
1822
- - `src/chat/channel-header/ChannelHeaderView.tsx` (customization wiring)
1823
- - Store/context entrypoints
1824
-
1825
- After UI changes in **live-chat-frontend**, run `pnpm sync` then `pnpm build`, bump version, publish.
1826
-
1827
- ### Local install in consumer app (e.g. Elstar)
1828
-
1829
- ```bash
1830
- cd chat-ui-package && pnpm pack --pack-destination .
1831
- ```
1832
-
1833
- In consumer `package.json`:
1834
-
1835
- ```json
1836
- "@realtimexsco/live-chat": "file:../path/to/realtimexsco-live-chat-1.1.0.tgz"
1837
- ```
1838
-
1839
- Then `pnpm install` in the consumer app.
1840
-
1841
- ### Version history (customization)
57
+ ## More information
1842
58
 
1843
- | Version | Change |
1844
- |---------|--------|
1845
- | **1.1.56** | Hardened default fallback (`resolveComponent` null check); drawer slots use optional customization hook |
1846
- | **1.1.55** | Drawer slots (`MembersDrawer`, `ChannelDetailsDrawer`, etc.) wired through `resolveComponent` |
1847
- | **1.1.53** | UI polish synced from frontend (bubble curves, drawer headers, destructive menu icons) |
59
+ | Topic | Link |
60
+ |-------|------|
61
+ | Full setup guide (Tailwind, Vite, Next.js, config) | [docs/full-guide.md](./docs/full-guide.md) |
62
+ | Customization (`components`, `classNames`, `features`) | [Customization](./docs/full-guide.md#customization--three-ways) |
63
+ | ChatMain props & API | [Props reference](./docs/full-guide.md#chatmain-props-reference) |
64
+ | Hooks & store | [Hooks & store](./docs/full-guide.md#hooks--store) |
65
+ | Dev proxy (CORS) | [Dev proxy](./docs/full-guide.md#dev-proxy-cors) |
66
+ | npm package | [npmjs.com/package/@realtimexsco/live-chat](https://www.npmjs.com/package/@realtimexsco/live-chat) |
67
+ | License | [MIT License](./LICENSE) |
1848
68
 
1849
69
  ---
1850
70
 
1851
71
  ## License
1852
72
 
1853
- [MIT](LICENSE)
73
+ [MIT](./LICENSE) © RealtimeX