@realtimexsco/live-chat 1.0.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/LICENSE +21 -0
- package/README.md +398 -0
- package/dist/index.cjs +7075 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.mts +701 -0
- package/dist/index.d.ts +701 -0
- package/dist/index.mjs +7034 -0
- package/dist/index.mjs.map +1 -0
- package/dist/styles.css +196 -0
- package/package.json +96 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 RealtimeX
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,398 @@
|
|
|
1
|
+
# @realtimexsco/live-chat
|
|
2
|
+
|
|
3
|
+
> Drop-in real-time chat UI for React — 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
|
+
[](https://www.npmjs.com/package/@realtimexsco/live-chat)
|
|
6
|
+
[](https://www.npmjs.com/package/@realtimexsco/live-chat)
|
|
7
|
+
[](https://www.npmjs.com/package/@realtimexsco/live-chat)
|
|
8
|
+
|
|
9
|
+
Use the all-in-one `<ChatMain />` for a complete chat app in minutes, or compose
|
|
10
|
+
the individual pieces (`<Chat>`, `<ChannelList>`, `<MessageList>`, …) for full
|
|
11
|
+
control over layout and styling.
|
|
12
|
+
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
## Table of contents
|
|
16
|
+
|
|
17
|
+
- [Features](#features)
|
|
18
|
+
- [Requirements](#requirements)
|
|
19
|
+
- [Installation](#installation)
|
|
20
|
+
- [Tailwind CSS v4 setup (required)](#tailwind-css-v4-setup-required)
|
|
21
|
+
- [Quick start — full UI](#quick-start--full-ui)
|
|
22
|
+
- [Composable usage (recommended)](#composable-usage-recommended)
|
|
23
|
+
- [How it works](#how-it-works)
|
|
24
|
+
- [`Chat` provider props](#chat-provider-props)
|
|
25
|
+
- [Exported components & hooks](#exported-components--hooks)
|
|
26
|
+
- [Theming](#theming)
|
|
27
|
+
- [Troubleshooting](#troubleshooting)
|
|
28
|
+
- [License](#license)
|
|
29
|
+
|
|
30
|
+
---
|
|
31
|
+
|
|
32
|
+
## Features
|
|
33
|
+
|
|
34
|
+
- 💬 **1-to-1 and group chats** with real-time delivery over Socket.IO
|
|
35
|
+
- ⚡ **Optimistic UI** — messages appear instantly, reconcile on server ack
|
|
36
|
+
- 😀 Reactions, replies, forwarding, pinned, starred & saved messages
|
|
37
|
+
- 🔎 Conversation + message search, attachments, member/admin management
|
|
38
|
+
- 🎨 **Themeable** via a single `themeColor` prop and CSS variables (light/dark)
|
|
39
|
+
- 🧩 **Two integration modes** — all-in-one component or composable building blocks
|
|
40
|
+
- 🦾 Ships TypeScript types and ESM + CJS builds
|
|
41
|
+
|
|
42
|
+
---
|
|
43
|
+
|
|
44
|
+
## Requirements
|
|
45
|
+
|
|
46
|
+
This package relies on the following **peer dependencies** in your app:
|
|
47
|
+
|
|
48
|
+
| Peer dependency | Version |
|
|
49
|
+
| --------------- | ------- |
|
|
50
|
+
| `react` | ≥ 17 (19 recommended) |
|
|
51
|
+
| `react-dom` | ≥ 17 |
|
|
52
|
+
| `zustand` | ≥ 4 |
|
|
53
|
+
| `tailwindcss` | **v4** (required) |
|
|
54
|
+
|
|
55
|
+
You also need, at runtime:
|
|
56
|
+
|
|
57
|
+
- **Client ID** — your RealtimeX client/app id
|
|
58
|
+
- **Access token** — JWT for the logged-in user
|
|
59
|
+
- **Logged user details** — `{ _id, name, email }`
|
|
60
|
+
- **API URL** — e.g. `https://your-server.com/api/v1`
|
|
61
|
+
- **Socket URL** — e.g. `https://your-server.com`
|
|
62
|
+
|
|
63
|
+
---
|
|
64
|
+
|
|
65
|
+
## Installation
|
|
66
|
+
|
|
67
|
+
```bash
|
|
68
|
+
npm install @realtimexsco/live-chat
|
|
69
|
+
# install peer dependencies if you don't have them yet
|
|
70
|
+
npm install react react-dom zustand tailwindcss @tailwindcss/vite
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
> Published on the public npm registry — no `.npmrc` or auth token needed.
|
|
74
|
+
|
|
75
|
+
---
|
|
76
|
+
|
|
77
|
+
## Tailwind CSS v4 setup (required)
|
|
78
|
+
|
|
79
|
+
The package ships Tailwind utility classes, so your app must (1) import the
|
|
80
|
+
package stylesheet and (2) scan the built package output so Tailwind generates
|
|
81
|
+
the classes it uses.
|
|
82
|
+
|
|
83
|
+
In your global stylesheet (e.g. `src/index.css`):
|
|
84
|
+
|
|
85
|
+
```css
|
|
86
|
+
@import "tailwindcss";
|
|
87
|
+
@import "@realtimexsco/live-chat/styles.css";
|
|
88
|
+
|
|
89
|
+
/* Scan the package dist so Tailwind emits the classes it uses */
|
|
90
|
+
@source "../node_modules/@realtimexsco/live-chat/dist/index.mjs";
|
|
91
|
+
@source "../node_modules/@realtimexsco/live-chat/dist/index.cjs";
|
|
92
|
+
|
|
93
|
+
:root {
|
|
94
|
+
--radius: 0.625rem;
|
|
95
|
+
|
|
96
|
+
/* Chat theme — overridden at runtime by the Chat `themeColor` prop */
|
|
97
|
+
--chat-theme: #3b82f6;
|
|
98
|
+
--chat-theme-5: #3b82f60d;
|
|
99
|
+
--chat-theme-10: #3b82f61a;
|
|
100
|
+
--chat-theme-20: #3b82f633;
|
|
101
|
+
--chat-theme-40: #3b82f666;
|
|
102
|
+
--chat-theme-60: #3b82f699;
|
|
103
|
+
}
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
If you use **Vite**, dedupe React/Zustand and pre-bundle the package:
|
|
107
|
+
|
|
108
|
+
```ts
|
|
109
|
+
// vite.config.ts
|
|
110
|
+
import { defineConfig } from "vite";
|
|
111
|
+
import react from "@vitejs/plugin-react";
|
|
112
|
+
import tailwindcss from "@tailwindcss/vite";
|
|
113
|
+
|
|
114
|
+
export default defineConfig({
|
|
115
|
+
plugins: [react(), tailwindcss()],
|
|
116
|
+
resolve: { dedupe: ["react", "react-dom", "zustand"] },
|
|
117
|
+
optimizeDeps: { include: ["@realtimexsco/live-chat", "zustand"] },
|
|
118
|
+
});
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
---
|
|
122
|
+
|
|
123
|
+
## Quick start — full UI
|
|
124
|
+
|
|
125
|
+
The fastest path. `ChatMain` renders the sidebar, chat area, and modals in one
|
|
126
|
+
component.
|
|
127
|
+
|
|
128
|
+
```tsx
|
|
129
|
+
import ChatMain from "@realtimexsco/live-chat";
|
|
130
|
+
|
|
131
|
+
export default function App() {
|
|
132
|
+
return (
|
|
133
|
+
<ChatMain
|
|
134
|
+
clientId="YOUR_CLIENT_ID"
|
|
135
|
+
accessToken="YOUR_JWT_TOKEN"
|
|
136
|
+
loggedUserDetails={{
|
|
137
|
+
_id: "USER_ID",
|
|
138
|
+
name: "John Doe",
|
|
139
|
+
email: "john@example.com",
|
|
140
|
+
}}
|
|
141
|
+
apiUrl="https://your-server.com/api/v1"
|
|
142
|
+
socketUrl="https://your-server.com"
|
|
143
|
+
themeColor="#3b82f6"
|
|
144
|
+
/>
|
|
145
|
+
);
|
|
146
|
+
}
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
---
|
|
150
|
+
|
|
151
|
+
## Composable usage (recommended)
|
|
152
|
+
|
|
153
|
+
Build your own layout from the exported pieces for full control over the
|
|
154
|
+
sidebar, routing, and styling.
|
|
155
|
+
|
|
156
|
+
```tsx
|
|
157
|
+
import { useState, useCallback } from "react";
|
|
158
|
+
import {
|
|
159
|
+
Chat,
|
|
160
|
+
Channel,
|
|
161
|
+
ChannelList,
|
|
162
|
+
ChannelHeader,
|
|
163
|
+
MessageList,
|
|
164
|
+
MessageInput,
|
|
165
|
+
LoggedUserDetails,
|
|
166
|
+
ForwardModal,
|
|
167
|
+
useChatContext,
|
|
168
|
+
useChatStore,
|
|
169
|
+
} from "@realtimexsco/live-chat";
|
|
170
|
+
|
|
171
|
+
function MainChatArea() {
|
|
172
|
+
const { activeChannel } = useChatContext();
|
|
173
|
+
const { setActiveChannel, error, status } = useChatStore();
|
|
174
|
+
|
|
175
|
+
const [search, setSearch] = useState("");
|
|
176
|
+
const [isMenuOpen, setIsMenuOpen] = useState(false);
|
|
177
|
+
const [replyToMessage, setReplyToMessage] = useState<any>(null);
|
|
178
|
+
const [forwardMessage, setForwardMessage] = useState<any>(null);
|
|
179
|
+
|
|
180
|
+
// ⚠️ Declare all hooks BEFORE any conditional return (see Troubleshooting)
|
|
181
|
+
const handleUserSelect = useCallback(
|
|
182
|
+
(channel: any) => {
|
|
183
|
+
setActiveChannel(channel);
|
|
184
|
+
setIsMenuOpen(false);
|
|
185
|
+
setReplyToMessage(null);
|
|
186
|
+
setForwardMessage(null);
|
|
187
|
+
},
|
|
188
|
+
[setActiveChannel]
|
|
189
|
+
);
|
|
190
|
+
|
|
191
|
+
if (error) return <div>Connection error: {error}</div>;
|
|
192
|
+
if (status === "connecting" || status === "idle") return <div>Loading chat…</div>;
|
|
193
|
+
|
|
194
|
+
return (
|
|
195
|
+
<div className="flex h-screen w-screen overflow-hidden">
|
|
196
|
+
<aside className="w-[400px] border-r flex flex-col">
|
|
197
|
+
<LoggedUserDetails
|
|
198
|
+
searchQuery={search}
|
|
199
|
+
setSearchQuery={setSearch}
|
|
200
|
+
isCreateModalOpen={isMenuOpen}
|
|
201
|
+
setIsCreateModalOpen={setIsMenuOpen}
|
|
202
|
+
onUserSelect={handleUserSelect}
|
|
203
|
+
/>
|
|
204
|
+
<ChannelList debouncedSearch={search} onNewChatClick={() => setIsMenuOpen(true)} />
|
|
205
|
+
</aside>
|
|
206
|
+
|
|
207
|
+
<main className="flex flex-col flex-1 min-w-0">
|
|
208
|
+
{/* Channel resolves the conversation, fetches messages, and joins the socket room */}
|
|
209
|
+
<Channel>
|
|
210
|
+
{activeChannel ? (
|
|
211
|
+
<>
|
|
212
|
+
<ChannelHeader />
|
|
213
|
+
<div className="flex-1 min-h-0">
|
|
214
|
+
<MessageList
|
|
215
|
+
onReply={(msg) => setReplyToMessage(msg)}
|
|
216
|
+
onForward={(msg) => setForwardMessage(msg)}
|
|
217
|
+
/>
|
|
218
|
+
</div>
|
|
219
|
+
<MessageInput
|
|
220
|
+
replyTo={replyToMessage}
|
|
221
|
+
onCancelReply={() => setReplyToMessage(null)}
|
|
222
|
+
onMessageSent={() => setReplyToMessage(null)}
|
|
223
|
+
/>
|
|
224
|
+
</>
|
|
225
|
+
) : (
|
|
226
|
+
<div className="flex h-full items-center justify-center text-gray-400">
|
|
227
|
+
Select a conversation
|
|
228
|
+
</div>
|
|
229
|
+
)}
|
|
230
|
+
</Channel>
|
|
231
|
+
</main>
|
|
232
|
+
|
|
233
|
+
{forwardMessage && (
|
|
234
|
+
<ForwardModal
|
|
235
|
+
isOpen
|
|
236
|
+
onClose={() => setForwardMessage(null)}
|
|
237
|
+
message={forwardMessage}
|
|
238
|
+
onForwardComplete={(channel) => setActiveChannel(channel)}
|
|
239
|
+
/>
|
|
240
|
+
)}
|
|
241
|
+
</div>
|
|
242
|
+
);
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
export default function App() {
|
|
246
|
+
return (
|
|
247
|
+
<Chat
|
|
248
|
+
client={{ id: "YOUR_CLIENT_ID" }}
|
|
249
|
+
accessToken="YOUR_JWT_TOKEN"
|
|
250
|
+
loggedUserDetails={{ _id: "USER_ID", name: "John Doe", email: "john@example.com" }}
|
|
251
|
+
apiUrl="https://your-server.com/api/v1"
|
|
252
|
+
socketUrl="https://your-server.com"
|
|
253
|
+
themeColor="#3b82f6"
|
|
254
|
+
onMessageReceived={(msg) => console.log("Background message:", msg)}
|
|
255
|
+
onSocketConnected={(id) => console.log("Socket connected:", id)}
|
|
256
|
+
onSocketError={(err) => console.error("Socket error:", err)}
|
|
257
|
+
>
|
|
258
|
+
<MainChatArea />
|
|
259
|
+
</Chat>
|
|
260
|
+
);
|
|
261
|
+
}
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
> **Important:** Wrap the active chat panel in `<Channel>`. It resolves the
|
|
265
|
+
> conversation id, fetches messages, and emits the correct group/DM socket
|
|
266
|
+
> events. Don't manually emit `group_join` / `group_message_read` yourself.
|
|
267
|
+
|
|
268
|
+
---
|
|
269
|
+
|
|
270
|
+
## How it works
|
|
271
|
+
|
|
272
|
+
```
|
|
273
|
+
┌─────────────────────────────────────────────────────────────────────┐
|
|
274
|
+
│ <Chat> — provider │
|
|
275
|
+
│ initializes auth (clientId + accessToken), REST API client, │
|
|
276
|
+
│ Socket.IO connection, and the Zustand store │
|
|
277
|
+
└───────────────┬───────────────────────────────────────────────────────┘
|
|
278
|
+
│ provides context + store
|
|
279
|
+
┌───────┴────────┐
|
|
280
|
+
▼ ▼
|
|
281
|
+
<ChannelList> <Channel> (wraps the active conversation)
|
|
282
|
+
lists convos 1. resolves conversationId (DM vs group)
|
|
283
|
+
click → sets 2. fetches message history via API
|
|
284
|
+
activeChannel 3. joins the socket room, marks messages read
|
|
285
|
+
│ │
|
|
286
|
+
▼ ▼
|
|
287
|
+
activeChannel <MessageList> renders the thread
|
|
288
|
+
in store <MessageInput> sends (optimistic UI)
|
|
289
|
+
│
|
|
290
|
+
▼
|
|
291
|
+
Socket events (new / edited / deleted message,
|
|
292
|
+
reactions, read receipts, forwards) update the
|
|
293
|
+
Zustand store → React re-renders automatically
|
|
294
|
+
```
|
|
295
|
+
|
|
296
|
+
**Lifecycle in short:**
|
|
297
|
+
|
|
298
|
+
1. `<Chat>` boots auth, the API client, the socket, and the store.
|
|
299
|
+
2. `<ChannelList>` shows conversations; selecting one calls `setActiveChannel`.
|
|
300
|
+
3. `<Channel>` reacts to the active channel — fetches messages, joins the socket
|
|
301
|
+
room, and marks them read.
|
|
302
|
+
4. `<MessageInput>` sends a message **optimistically** (it shows immediately),
|
|
303
|
+
then reconciles with the server acknowledgement.
|
|
304
|
+
5. Incoming socket events mutate the Zustand store, and the UI updates reactively.
|
|
305
|
+
|
|
306
|
+
---
|
|
307
|
+
|
|
308
|
+
## `Chat` provider props
|
|
309
|
+
|
|
310
|
+
| Prop | Type | Required | Description |
|
|
311
|
+
| ---- | ---- | -------- | ----------- |
|
|
312
|
+
| `client` | `{ id: string }` | Yes | RealtimeX client / app id |
|
|
313
|
+
| `accessToken` | `string` | Yes | JWT access token |
|
|
314
|
+
| `loggedUserDetails` | `{ _id, name, email, ... }` | Yes | Current user profile |
|
|
315
|
+
| `apiUrl` | `string` | No | REST API base (default: `http://localhost:5000/api/v1`) |
|
|
316
|
+
| `socketUrl` | `string` | No | Socket server URL (default: `http://localhost:5000`) |
|
|
317
|
+
| `themeColor` | `string` | No | Primary chat accent color (default: `#7494ec`) |
|
|
318
|
+
| `theme` | `"light" \| "dark"` | No | Color scheme |
|
|
319
|
+
| `uiConfig` | `UIConfig` | No | Sidebar / header UI toggles |
|
|
320
|
+
| `onMessageReceived` | `(msg) => void` | No | Fired when a message arrives in a non-active chat |
|
|
321
|
+
| `onSocketConnected` | `(socketId) => void` | No | Socket connected callback |
|
|
322
|
+
| `onSocketError` | `(error) => void` | No | Socket error callback |
|
|
323
|
+
| `children` | `ReactNode` | Yes | Your chat layout |
|
|
324
|
+
|
|
325
|
+
### `UIConfig`
|
|
326
|
+
|
|
327
|
+
```ts
|
|
328
|
+
{
|
|
329
|
+
showSidebarAvatar?: boolean; // default: true
|
|
330
|
+
sidebarAvatarColor?: string;
|
|
331
|
+
showChannelDetails?: boolean; // default: true
|
|
332
|
+
messageDateFormat?: (date: string) => string;
|
|
333
|
+
}
|
|
334
|
+
```
|
|
335
|
+
|
|
336
|
+
---
|
|
337
|
+
|
|
338
|
+
## Exported components & hooks
|
|
339
|
+
|
|
340
|
+
| Export | Description |
|
|
341
|
+
| ------ | ----------- |
|
|
342
|
+
| `ChatMain` | All-in-one chat UI (default export) |
|
|
343
|
+
| `Chat` | Provider — initializes auth, API, socket, store |
|
|
344
|
+
| `Channel` | Active conversation lifecycle (fetch, join, mark-read) |
|
|
345
|
+
| `ChannelList` | Conversation sidebar list |
|
|
346
|
+
| `ChannelHeader` | Chat header with search, details, actions |
|
|
347
|
+
| `MessageList` | Message thread |
|
|
348
|
+
| `MessageInput` | Compose box |
|
|
349
|
+
| `LoggedUserDetails` | User profile + new chat / create group |
|
|
350
|
+
| `ForwardModal` | Forward message modal |
|
|
351
|
+
| `useChatContext` | `{ activeChannel, setActiveChannel, themeColor, uiConfig }` |
|
|
352
|
+
| `useChatStore` | Full Zustand store (messages, socket actions, status, error) |
|
|
353
|
+
| `socketService` | Low-level socket service |
|
|
354
|
+
| `setChatClientId`, `setChatAccessToken`, `setChatBaseURL` | Manual API config |
|
|
355
|
+
|
|
356
|
+
---
|
|
357
|
+
|
|
358
|
+
## Theming
|
|
359
|
+
|
|
360
|
+
Set a single accent color via the `themeColor` prop; it drives the
|
|
361
|
+
`--chat-theme*` CSS variables. Override any of the variables shown in the
|
|
362
|
+
[Tailwind setup](#tailwind-css-v4-setup-required) to customize further, and pass
|
|
363
|
+
`theme="dark"` for dark mode.
|
|
364
|
+
|
|
365
|
+
```tsx
|
|
366
|
+
<Chat themeColor="#7c3aed" theme="dark" /* … */>{/* … */}</Chat>
|
|
367
|
+
```
|
|
368
|
+
|
|
369
|
+
---
|
|
370
|
+
|
|
371
|
+
## Troubleshooting
|
|
372
|
+
|
|
373
|
+
**`Rendered more hooks than during the previous render`**
|
|
374
|
+
Declare all hooks (`useState`, `useCallback`, …) at the **top** of the component,
|
|
375
|
+
before any conditional `return`. Handle loading/error states after the hooks.
|
|
376
|
+
|
|
377
|
+
**Styles missing / unstyled components**
|
|
378
|
+
1. Confirm the `@source` paths in your CSS point to the package `dist/` files.
|
|
379
|
+
2. Import `@realtimexsco/live-chat/styles.css`.
|
|
380
|
+
3. Restart your dev server after installing or updating the package.
|
|
381
|
+
|
|
382
|
+
**Chat loads but messages don't appear**
|
|
383
|
+
Ensure `<Channel>` wraps `MessageList` and `MessageInput`. The package keys
|
|
384
|
+
messages by `conversationId` internally — don't key DM messages by user id.
|
|
385
|
+
|
|
386
|
+
**Group events firing in DM (or vice versa)**
|
|
387
|
+
Always use the composable pattern with `<Channel>`, and don't manually emit
|
|
388
|
+
`group_join` / `group_message_read` outside the package.
|
|
389
|
+
|
|
390
|
+
**Duplicate React / invalid hook call**
|
|
391
|
+
Add `resolve: { dedupe: ["react", "react-dom", "zustand"] }` to your
|
|
392
|
+
`vite.config.ts`.
|
|
393
|
+
|
|
394
|
+
---
|
|
395
|
+
|
|
396
|
+
## License
|
|
397
|
+
|
|
398
|
+
[MIT](https://www.npmjs.com/package/@realtimexsco/live-chat) © RealtimeX
|