@realtimexsco/live-chat 1.2.0 → 1.3.0

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