@realtimexsco/live-chat 1.3.0 → 1.3.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,1672 +1,73 @@
1
1
  # @realtimexsco/live-chat
2
2
 
3
- > Complete real-time chat UI for **React** and **Next.js**DMs, groups, reactions, replies, forwarding, pins, stars, attachments, search, light/dark theme, locale/timezone, and **full UI customization** (replace the whole chat or any single component).
3
+ > Drop-in real-time chat UI for React and Next.js — direct messages, group chats, reactions, replies, forwarding, pinned/starred messages, and more. Built on Socket.IO, Zustand, Tailwind CSS v4, and Radix UI.
4
+
5
+ [![npm version](https://img.shields.io/npm/v/@realtimexsco/live-chat.svg)](https://www.npmjs.com/package/@realtimexsco/live-chat)
6
+ [![npm downloads](https://img.shields.io/npm/dm/@realtimexsco/live-chat.svg)](https://www.npmjs.com/package/@realtimexsco/live-chat)
7
+ [![license](https://img.shields.io/npm/l/@realtimexsco/live-chat.svg)](./LICENSE)
4
8
 
5
9
  | | |
6
10
  |---|---|
7
- | **Package name** | `@realtimexsco/live-chat` |
8
- | **Registry** | [npm](https://www.npmjs.com/) (public — no `.npmrc` required) |
9
- | **Styles entry** | `@realtimexsco/live-chat/styles.css` |
11
+ | **Package** | [`@realtimexsco/live-chat`](https://www.npmjs.com/package/@realtimexsco/live-chat) |
12
+ | **Styles** | `@realtimexsco/live-chat/styles.css` |
10
13
  | **Requires** | React 17+, Tailwind CSS **v4**, Zustand 4+ |
11
- | **Current version** | `1.1.0` |
12
-
13
- Socket.IO client, REST API client, Zustand store, Radix UI, emoji picker, and all chat UI are **bundled inside the package**. Your app only installs a few peer dependencies.
14
-
15
- ---
16
-
17
- ## Start here (5-minute overview)
18
-
19
- 1. **Install** the package + peers (`react`, `react-dom`, `zustand`, `tailwindcss` v4).
20
- 2. **Import CSS** — `@import "@realtimexsco/live-chat/styles.css"` and add Tailwind `@source` for the package `dist` files.
21
- 3. **Create** `chat.config.ts` with `clientId`, `accessToken`, `loggedUserDetails`, `apiUrl`, `socketUrl`.
22
- 4. **Render** `<ChatMain {...chatConfig} />` — the package includes a built-in viewport wrapper. For admin templates use `viewportHeight="calc(100dvh - 4rem)"`; for full-page chat use `viewportHeight="screen"`.
23
- 5. **Optional** — pass `components`, `classNames`, or `features` to customize. **Omit any slot → package default is used automatically.**
24
-
25
- ```tsx
26
- // Full page
27
- <ChatMain {...chatConfig} viewportHeight="screen" />
28
-
29
- // Admin shell with 4rem header (Elstar, Metronic, etc.)
30
- <ChatMain {...chatConfig} viewportHeight="calc(100dvh - 4rem)" />
31
-
32
- // Inside your own bounded flex column (parent must have height + min-h-0)
33
- <div className="flex min-h-0 flex-1 overflow-hidden">
34
- <ChatMain {...chatConfig} viewportHeight="full" />
35
- </div>
36
- ```
37
-
38
- **Do not** pass empty or placeholder components for slots you want to keep default — simply omit those keys from `components`.
39
-
40
- ---
41
-
42
- ## Table of contents
43
-
44
- 1. [Chat features included](#chat-features-included)
45
- 2. [What you install vs what is bundled](#what-you-install-vs-what-is-bundled)
46
- 3. [Backend credentials](#backend-credentials)
47
- 4. [Installation step by step](#installation-step-by-step)
48
- 5. [Tailwind CSS v4 setup (required)](#tailwind-css-v4-setup-required)
49
- 6. [Project config file](#project-config-file)
50
- 7. [Quick start — minimum working chat](#quick-start--minimum-working-chat)
51
- 8. [Complete ChatMain example](#complete-chatmain-example)
52
- 9. [Customization — three ways](#customization--three-ways)
53
- 10. [Default component fallback (important)](#default-component-fallback-important)
54
- 11. [Which slots are wired](#which-slots-are-wired)
55
- 12. [Production integration pattern (admin templates)](#production-integration-pattern-admin-templates)
56
- 13. [Customization examples (Case 1–3)](#customization-examples-case-13)
57
- 14. [Phone & video calling](#phone--video-calling)
58
- 15. [Theme, light/dark, date/time](#theme-lightdark-datetime)
59
- 16. [Layout sections (`layout` prop)](#layout-sections-layout-prop)
60
- 17. [All component slots + examples](#all-component-slots--examples)
61
- 18. [All classNames keys](#all-classnames-keys)
62
- 19. [Wrapping package defaults (`packageDefaultComponents`)](#wrapping-package-defaults-packagedefaultcomponents)
63
- 20. [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)
76
-
77
- ---
78
-
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 |
100
-
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 |
117
-
118
- ### Bundled inside the package (do not install separately)
119
-
120
- `axios`, `socket.io-client`, `radix-ui`, `lucide-react`, `emoji-picker-react`, `clsx`, `tailwind-merge`, `class-variance-authority`, and all chat components.
121
-
122
- ---
123
-
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) |
14
+ | **License** | [MIT](./LICENSE) |
136
15
 
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.
16
+ Socket.IO, REST client, Zustand store, Radix UI, emoji picker, and chat UI are **bundled**. Your app only installs peer dependencies.
138
17
 
139
18
  ---
140
19
 
141
- ## Installation step by step
142
-
143
- ### 1. Install the package and peers
144
-
145
- **npm:**
20
+ ## Install
146
21
 
147
22
  ```bash
148
23
  npm install @realtimexsco/live-chat react react-dom zustand tailwindcss
149
24
  ```
150
25
 
151
- **pnpm:**
152
-
153
- ```bash
154
- pnpm add @realtimexsco/live-chat react react-dom zustand tailwindcss
155
- ```
156
-
157
- ### 2. Install Tailwind v4 plugin for your framework
158
-
159
- **Vite:**
160
-
161
- ```bash
162
- pnpm add -D @tailwindcss/vite @vitejs/plugin-react vite
163
- ```
164
-
165
- **Next.js:**
166
-
167
- ```bash
168
- pnpm add -D @tailwindcss/postcss
169
- # next is usually already installed
170
- ```
171
-
172
- ### 3. Verify install
173
-
174
- ```bash
175
- npm ls @realtimexsco/live-chat
176
- ls node_modules/@realtimexsco/live-chat/dist/index.d.ts # TypeScript types must exist
177
- ```
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
-
185
- ---
186
-
187
- ## Tailwind CSS v4 setup (required)
188
-
189
- The package will look unstyled without these three steps:
190
-
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`
194
-
195
- ### Vite — `src/index.css`
196
-
197
- ```css
198
- @import "tailwindcss";
199
- @import "@realtimexsco/live-chat/styles.css";
200
-
201
- @source "../node_modules/@realtimexsco/live-chat/dist/index.mjs";
202
- @source "../node_modules/@realtimexsco/live-chat/dist/index.cjs";
203
- @source "../**/*.{js,jsx,ts,tsx}";
204
-
205
- :root {
206
- --radius: 0.625rem;
207
- --background: oklch(1 0 0);
208
- --foreground: oklch(0.145 0 0);
209
- --primary: oklch(0.205 0 0);
210
- --primary-foreground: oklch(0.985 0 0);
211
- --muted: oklch(0.97 0 0);
212
- --muted-foreground: oklch(0.556 0 0);
213
- --border: oklch(0.922 0 0);
214
- --ring: oklch(0.708 0 0);
215
- --destructive: oklch(0.577 0.245 27.325);
216
- --card: oklch(1 0 0);
217
- --card-foreground: oklch(0.145 0 0);
218
- --popover: oklch(1 0 0);
219
- --popover-foreground: oklch(0.145 0 0);
220
- --accent: oklch(0.97 0 0);
221
- --accent-foreground: oklch(0.205 0 0);
222
- --secondary: oklch(0.97 0 0);
223
- --secondary-foreground: oklch(0.205 0 0);
224
- --input: oklch(0.922 0 0);
225
-
226
- /* Overridden at runtime by ChatMain `themeColor` */
227
- --chat-theme: #6366f1;
228
- --chat-theme-5: #6366f10D;
229
- --chat-theme-10: #6366f11A;
230
- --chat-theme-20: #6366f133;
231
- --chat-theme-40: #6366f166;
232
- --chat-theme-60: #6366f199;
233
- }
234
-
235
- @theme inline {
236
- --color-background: var(--background);
237
- --color-foreground: var(--foreground);
238
- --color-primary: var(--primary);
239
- --color-primary-foreground: var(--primary-foreground);
240
- --color-muted: var(--muted);
241
- --color-muted-foreground: var(--muted-foreground);
242
- --color-border: var(--border);
243
- --color-ring: var(--ring);
244
- --color-destructive: var(--destructive);
245
- --color-card: var(--card);
246
- --color-card-foreground: var(--card-foreground);
247
- --color-popover: var(--popover);
248
- --color-popover-foreground: var(--popover-foreground);
249
- --color-accent: var(--accent);
250
- --color-accent-foreground: var(--accent-foreground);
251
- --color-secondary: var(--secondary);
252
- --color-secondary-foreground: var(--secondary-foreground);
253
- --color-input: var(--input);
254
- --color-chat-theme: var(--chat-theme);
255
- --color-chat-theme-5: var(--chat-theme-5);
256
- --color-chat-theme-10: var(--chat-theme-10);
257
- --color-chat-theme-20: var(--chat-theme-20);
258
- --color-chat-theme-40: var(--chat-theme-40);
259
- --color-chat-theme-60: var(--chat-theme-60);
260
- }
261
-
262
- @layer base {
263
- html, body, #root { height: 100%; }
264
- body { @apply bg-background text-foreground; }
265
- }
266
- ```
267
-
268
- ### Next.js — `src/app/globals.css`
269
-
270
- > `globals.css` is inside `src/app/`, so `@source` uses **`../../node_modules`** (not `../`).
271
-
272
- ```css
273
- @import "tailwindcss";
274
- @import "@realtimexsco/live-chat/styles.css";
275
-
276
- @source "../../node_modules/@realtimexsco/live-chat/dist/index.mjs";
277
- @source "../../node_modules/@realtimexsco/live-chat/dist/index.cjs";
278
-
279
- /* Copy :root, @theme inline, and @layer base from the Vite example above */
280
- ```
281
-
282
- ### `@source` path cheat sheet
283
-
284
- | CSS file location | `@source` prefix |
285
- |-------------------|------------------|
286
- | `src/index.css` (Vite) | `../node_modules/...` |
287
- | `src/app/globals.css` (Next.js) | `../../node_modules/...` |
288
-
289
- Wrong path = broken or unstyled chat UI.
290
-
291
- ### Vite — `vite.config.ts`
292
-
293
- ```ts
294
- import path from "path";
295
- import { fileURLToPath } from "url";
296
- import { defineConfig } from "vite";
297
- import react from "@vitejs/plugin-react";
298
- import tailwindcss from "@tailwindcss/vite";
299
-
300
- const __dirname = path.dirname(fileURLToPath(import.meta.url));
301
-
302
- export default defineConfig({
303
- plugins: [react(), tailwindcss()],
304
- resolve: {
305
- alias: { "@": path.resolve(__dirname, "src") },
306
- dedupe: ["react", "react-dom", "zustand", "socket.io-client", "@realtimexsco/live-chat"],
307
- },
308
- optimizeDeps: {
309
- exclude: ["@realtimexsco/live-chat"],
310
- include: ["zustand", "socket.io-client"],
311
- },
312
- });
313
- ```
314
-
315
- ### Next.js — `next.config.ts`
316
-
317
- ```ts
318
- import type { NextConfig } from "next";
319
-
320
- const nextConfig: NextConfig = {
321
- transpilePackages: ["@realtimexsco/live-chat"],
322
- };
323
-
324
- export default nextConfig;
325
- ```
326
-
327
- ### Next.js — `postcss.config.mjs`
328
-
329
- ```js
330
- const config = { plugins: { "@tailwindcss/postcss": {} } };
331
- export default config;
332
- ```
333
-
334
- ---
335
-
336
- ## Project config file
337
-
338
- Create `src/configs/chat.config.ts`:
339
-
340
- ```ts
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
- }
352
-
353
- const accessToken = "YOUR_JWT_ACCESS_TOKEN";
354
-
355
- export const chatConfig = {
356
- clientId: "YOUR_CLIENT_ID",
357
- accessToken,
358
- loggedUserDetails: {
359
- _id: userIdFromToken(accessToken) ?? "YOUR_USER_ID",
360
- name: "Your Name",
361
- email: "you@example.com",
362
- },
363
- apiUrl: "https://realtimex.softwareco.com/api/v1",
364
- socketUrl: "https://realtimex.softwareco.com", // optional — auto-derived from apiUrl
365
- themeColor: "#6366f1",
366
- } as const;
367
- ```
368
-
369
- ---
370
-
371
- ## Quick start — minimum working chat
372
-
373
- ```tsx
374
- "use client"; // Next.js only — required on any file that imports chat
375
-
376
- import ChatMain from "@realtimexsco/live-chat";
377
- import { chatConfig } from "@/configs/chat.config";
378
-
379
- export default function ChatPage() {
380
- return (
381
- <div className="h-dvh min-h-0 overflow-hidden">
382
- <ChatMain
383
- clientId={chatConfig.clientId}
384
- accessToken={chatConfig.accessToken}
385
- loggedUserDetails={chatConfig.loggedUserDetails}
386
- apiUrl={chatConfig.apiUrl}
387
- socketUrl={chatConfig.socketUrl}
388
- themeColor={chatConfig.themeColor}
389
- />
390
- </div>
391
- );
392
- }
393
- ```
394
-
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"`.
396
-
397
- ---
398
-
399
- ## Complete ChatMain example
400
-
401
- All common props: theme, locale, layout, customization, calls, alerts.
402
-
403
- ```tsx
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";
415
- import { chatConfig } from "@/configs/chat.config";
416
-
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
-
454
- return (
455
- <div className="h-dvh min-h-0 overflow-hidden">
456
- <ChatMain
457
- clientId={chatConfig.clientId}
458
- accessToken={chatConfig.accessToken}
459
- loggedUserDetails={chatConfig.loggedUserDetails}
460
- apiUrl={chatConfig.apiUrl}
461
- socketUrl={chatConfig.socketUrl}
462
- themeColor={chatConfig.themeColor}
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
- />
477
- </div>
478
- );
479
- }
480
- ```
481
-
482
- ---
483
-
484
- ## Customization — three ways
485
-
486
- You can combine all three.
487
-
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 |
493
-
494
- Omit any slot or className key → **package default is used automatically**.
495
-
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)
513
- ```
514
-
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
525
-
526
- Every wired slot follows this pattern:
527
-
528
- ```tsx
529
- const MembersDrawerSlot = resolveComponent("MembersDrawer", MembersDrawer);
530
- return <MembersDrawerSlot isOpen={...} onOpenChange={...} ... />;
531
- ```
532
-
533
- Your custom component receives the **same props** as the package default (`MembersDrawerSlotProps`, etc.). Import the prop type from the package for TypeScript.
534
-
535
- ### Wrapping instead of rebuilding
536
-
537
- Use `packageDefaultComponents` when you only need to restyle or add a wrapper:
538
-
539
- ```tsx
540
- import { packageDefaultComponents, type MembersDrawerSlotProps } from "@realtimexsco/live-chat";
541
-
542
- const MyMembersDrawer = (props: MembersDrawerSlotProps) => (
543
- <div className="my-drawer-shell">
544
- <packageDefaultComponents.MembersDrawer! {...props} />
545
- </div>
546
- );
547
- ```
548
-
549
- ---
550
-
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.
595
-
596
- ---
597
-
598
- ## Production integration pattern (admin templates)
599
-
600
- Recommended structure for apps like **Elstar**, **Metronic**, or any admin shell:
601
-
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
- ```
614
-
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,
647
- };
648
-
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
- };
656
- ```
657
-
658
- ### `views/Chat.tsx` — single entry
659
-
660
- ```tsx
661
- import ChatMain, { type ChatColorMode } from "@realtimexsco/live-chat";
662
- import { chatConfig } from "@/configs/chat.config";
663
- import { chatComponents, chatClassNames } from "@/configs/chat.customization.config";
664
- import { chatLayoutConfig } from "@/configs/chat.layout.config";
665
-
666
- export default function ChatPage() {
667
- return (
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
- />
677
- </div>
678
- );
679
- }
680
- ```
681
-
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.
722
-
723
- ---
724
-
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();
760
-
761
- return (
762
- <div className="flex h-full">
763
- {/* Your sidebar, header, messages, input — full control */}
764
- </div>
765
- );
766
- }
767
-
768
- <ChatMain
769
- {...chatConfig}
770
- clientId={chatConfig.clientId}
771
- components={{ ChatLayout: MyChatShell }}
772
- />;
773
- ```
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
- />
790
- ```
791
-
792
- ### Case 3 — Style with classNames only
793
-
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
- />
805
- ```
806
-
807
- ---
808
-
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);
821
- },
822
- onVideoCall: ({ activeChannel, conversationId }) => {
823
- yourVideoSDK.start(activeChannel.id, conversationId);
824
- },
825
- }}
826
- ```
827
-
828
- ### Option B — Replace the call button group
829
-
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
- }}
839
- ```
840
-
841
- > Do **not** import `HeaderCallActions` from the package inside your custom `HeaderCallActions` — that causes infinite recursion. Render your own buttons instead.
842
-
843
- ### Option C — Replace individual buttons
844
-
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
- }}
854
- ```
855
-
856
- ### Hide calls
857
-
858
- ```tsx
859
- features={{ showPhoneCall: false, showVideoCall: false }}
860
- ```
861
-
862
- ---
863
-
864
- ## Theme, light/dark, date/time
865
-
866
- ### Brand color
867
-
868
- ```tsx
869
- <ChatMain {...chatConfig} clientId={chatConfig.clientId} themeColor="#7c3aed" />
870
- ```
871
-
872
- Runtime updates `--chat-theme` CSS variables automatically.
873
-
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
- />
894
- ```
895
-
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
- ```
925
-
926
- ---
927
-
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)
973
-
974
- ```tsx
975
- import { useChatStore, type MembersDrawerSlotProps } from "@realtimexsco/live-chat";
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
- };
1004
- ```
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
- );
1017
- ```
1018
-
1019
- **Avatar**
1020
-
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
- );
1030
- ```
1031
-
1032
- **MessageItem** (custom bubble)
1033
-
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
- );
1043
- ```
1044
-
1045
- **ChannelListItem**
1046
-
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
- );
1060
- ```
1061
-
1062
- **MessageInput**
1063
-
1064
- ```tsx
1065
- import type { ChannelMessageBoxSlotProps } from "@realtimexsco/live-chat";
1066
-
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
- );
1078
- };
1079
- ```
1080
-
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>
1149
- );
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
- };
1157
- ```
1158
-
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.
1205
-
1206
- ```tsx
1207
- "use client";
1208
-
1209
- import { Chat, ChatLayout, type ChatComponents } from "@realtimexsco/live-chat";
1210
- import { chatConfig } from "@/configs/chat.config";
1211
-
1212
- const components: ChatComponents = {
1213
- Avatar: MyAvatar,
1214
- };
1215
-
1216
- export default function ChatPage() {
1217
- return (
1218
- <div className="h-dvh min-h-0 overflow-hidden">
1219
- <Chat
1220
- client={{ id: chatConfig.clientId }}
1221
- accessToken={chatConfig.accessToken}
1222
- loggedUserDetails={chatConfig.loggedUserDetails}
1223
- apiUrl={chatConfig.apiUrl}
1224
- socketUrl={chatConfig.socketUrl}
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
- }}
1233
- >
1234
- <ChatLayout themeColor={chatConfig.themeColor} />
1235
- </Chat>
1236
- </div>
1237
- );
1238
- }
1239
- ```
1240
-
1241
- ### `Chat` provider props
1242
-
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 />` |
1256
-
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);
1277
- ```
1278
-
1279
- ---
1280
-
1281
- ## React (Vite) — full setup
1282
-
1283
- ### 1. Create project
1284
-
1285
- ```bash
1286
- pnpm create vite my-chat-app --template react-ts
1287
- cd my-chat-app
1288
- ```
1289
-
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
1341
-
1342
- ```bash
1343
- pnpm dev
1344
- ```
26
+ **Vite:** `npm i -D @tailwindcss/vite` · **Next.js:** `npm i -D @tailwindcss/postcss`
1345
27
 
1346
28
  ---
1347
29
 
1348
- ## Next.js — full setup
1349
-
1350
- ### 1. Create project
1351
-
1352
- ```bash
1353
- pnpm create next-app@latest my-chat-app --typescript --tailwind --app --src-dir
1354
- cd my-chat-app
1355
- ```
1356
-
1357
- ### 2. Install
30
+ ## Quick start
1358
31
 
1359
- ```bash
1360
- pnpm add @realtimexsco/live-chat zustand
1361
- pnpm add -D @tailwindcss/postcss
1362
- ```
1363
-
1364
- ### 3. `src/app/chat/page.tsx`
32
+ 1. Import CSS — `@import "@realtimexsco/live-chat/styles.css"` and add Tailwind `@source` for the package `dist` files.
33
+ 2. Create `chat.config.ts` with `clientId`, `accessToken`, `loggedUserDetails`, `apiUrl`, `socketUrl`.
34
+ 3. Render `<ChatMain {...chatConfig} />`.
1365
35
 
1366
36
  ```tsx
1367
- "use client";
1368
-
1369
37
  import ChatMain from "@realtimexsco/live-chat";
1370
38
  import { chatConfig } from "@/configs/chat.config";
1371
39
 
1372
40
  export default function ChatPage() {
1373
41
  return (
1374
42
  <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
- />
43
+ <ChatMain {...chatConfig} viewportHeight="screen" />
1383
44
  </div>
1384
45
  );
1385
46
  }
1386
47
  ```
1387
48
 
1388
- ### 4. Run
1389
-
1390
- ```bash
1391
- pnpm dev
1392
- ```
1393
-
1394
- Open `http://localhost:3000/chat`
1395
-
1396
- ---
1397
-
1398
- ## Environment variables
1399
-
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
- ```
1408
-
1409
- ```ts
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
- // ...
1418
- };
1419
- ```
1420
-
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`:**
1430
-
1431
- ```ts
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
- },
1441
- ```
1442
-
1443
- Then set `apiUrl: "/chat-api"` in `chat.config.ts` for local development.
1444
-
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)
1463
- ```
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
-
1479
- ---
1480
-
1481
- ## All exports
1482
-
1483
- ```ts
1484
- import ChatMain, {
1485
- // ── Providers & layout ──
1486
- Chat,
1487
- ChatLayout,
1488
- DefaultChatLayout,
1489
- Channel,
1490
- ChatThemeProvider,
1491
- ChatLocaleProvider,
1492
- ChatAlertsProvider,
1493
- ChatCustomizationProvider,
1494
-
1495
- // ── UI components (standalone or customization reference) ──
1496
- ChannelList,
1497
- ChannelHeader,
1498
- MessageList,
1499
- MessageInput,
1500
- LoggedUserDetails,
1501
- ForwardModal,
1502
- MessageItem,
1503
- ChatAvatar,
1504
- DefaultChatAvatar,
1505
- ChannelListItem,
1506
- DefaultChannelListItem,
1507
- HeaderCallActions,
1508
- packageDefaultComponents,
1509
-
1510
- // ── Hooks ──
1511
- useChatStore,
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,
1535
- getSocketConfig,
1536
- showNotification,
1537
- downloadFile,
1538
-
1539
- // ── Types ──
1540
- type ChatComponents,
1541
- type ChatClassNames,
1542
- type ChatFeatures,
1543
- type ChatCallContext,
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
1564
- } from "@realtimexsco/live-chat";
1565
- ```
1566
-
1567
- **CSS import (not from JS):**
1568
-
1569
- ```css
1570
- @import "@realtimexsco/live-chat/styles.css";
1571
- ```
1572
-
1573
- ---
1574
-
1575
- ## Troubleshooting
1576
-
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 |
1597
-
1598
49
  ---
1599
50
 
1600
- ## Setup checklist
1601
-
1602
- Copy this list when integrating into a new project:
51
+ ## Features
1603
52
 
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
53
+ DMs · groups · Socket.IO · reactions · replies · forward · edit/delete · pin/star · attachments · search · typing · read receipts · light/dark · locale/timezone · full UI customization
1623
54
 
1624
55
  ---
1625
56
 
1626
- ## Package development (maintainers)
1627
-
1628
- This package is built from the **live-chat-frontend** reference app. Source lives in `src/`; published output is `dist/`.
1629
-
1630
- ### Sync from frontend
1631
-
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/
1636
- ```
1637
-
1638
- `scripts/sync-from-frontend.mjs` copies UI from the frontend repo. Protected files (not overwritten):
1639
-
1640
- - `src/index.ts`, `ChatMain.tsx`, `ChatContext.tsx`, `DefaultChatLayout.tsx`
1641
- - `src/chat/channel-header/ChannelHeaderView.tsx` (customization wiring)
1642
- - Store/context entrypoints
1643
-
1644
- After UI changes in **live-chat-frontend**, run `pnpm sync` then `pnpm build`, bump version, publish.
1645
-
1646
- ### Local install in consumer app (e.g. Elstar)
1647
-
1648
- ```bash
1649
- cd chat-ui-package && pnpm pack --pack-destination .
1650
- ```
1651
-
1652
- In consumer `package.json`:
1653
-
1654
- ```json
1655
- "@realtimexsco/live-chat": "file:../path/to/realtimexsco-live-chat-1.1.0.tgz"
1656
- ```
1657
-
1658
- Then `pnpm install` in the consumer app.
1659
-
1660
- ### Version history (customization)
57
+ ## More information
1661
58
 
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) |
59
+ | Topic | Link |
60
+ |-------|------|
61
+ | Full setup guide (Tailwind, Vite, Next.js, config) | [docs/full-guide.md](./docs/full-guide.md) |
62
+ | Customization (`components`, `classNames`, `features`) | [Customization](./docs/full-guide.md#customization--three-ways) |
63
+ | ChatMain props & API | [Props reference](./docs/full-guide.md#chatmain-props-reference) |
64
+ | Hooks & store | [Hooks & store](./docs/full-guide.md#hooks--store) |
65
+ | Dev proxy (CORS) | [Dev proxy](./docs/full-guide.md#dev-proxy-cors) |
66
+ | npm package | [npmjs.com/package/@realtimexsco/live-chat](https://www.npmjs.com/package/@realtimexsco/live-chat) |
67
+ | License | [MIT License](./LICENSE) |
1667
68
 
1668
69
  ---
1669
70
 
1670
71
  ## License
1671
72
 
1672
- [MIT](LICENSE)
73
+ [MIT](./LICENSE) © RealtimeX