urasoft-live-support 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/README.md ADDED
@@ -0,0 +1,1413 @@
1
+ # @urasoft/live-chat-widget
2
+
3
+ <div align="center">
4
+
5
+ [![npm version](https://img.shields.io/npm/v/@urasoft/live-chat-widget.svg)](https://www.npmjs.com/package/@urasoft/live-chat-widget)
6
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
7
+ [![TypeScript](https://img.shields.io/badge/TypeScript-5.3-blue)](https://www.typescriptlang.org/)
8
+
9
+ **Universal live chat widget library for web applications**
10
+
11
+ A modern, TypeScript-based chat widget with SignalR support, styled with Tailwind CSS. Works seamlessly with Vue, React, Next.js, Angular, or vanilla JavaScript.
12
+
13
+ [Features](#-features) • [Installation](#-installation) • [Quick Start](#-quick-start) • [Documentation](#-documentation)
14
+
15
+ </div>
16
+
17
+ ---
18
+
19
+ ## 🚀 Features
20
+
21
+ - 💬 **Framework Agnostic** - Works with Vue, React, Next.js, Angular, or vanilla JavaScript
22
+ - 🔌 **Built-in SignalR** - Real-time communication with configurable Hub connection
23
+ - 🌐 **HTTP REST API Support** - Send messages via HTTP endpoints with custom body mapping
24
+ - 🔄 **Hybrid Mode** - Send via HTTP, receive via SignalR (perfect for existing APIs)
25
+ - 🔄 **Smart Reconnection** - Multiple reconnection strategies (exponential, linear, fixed, custom)
26
+ - 📡 **10+ Event Callbacks** - Full control over connection lifecycle
27
+ - 🎨 **Fully Customizable** - Position, colors, messages, and styling
28
+ - 📱 **Responsive Design** - Mobile and desktop-friendly
29
+ - ⚡ **Lightweight** - Minimal dependencies, optimized bundle size
30
+ - 🎯 **TypeScript First** - Complete type definitions with IntelliSense support
31
+ - 🌐 **Flexible Configuration** - Headers (static/dynamic), query params, JWT token support
32
+ - 🎛️ **Transport Options** - WebSockets, Server-Sent Events, or Long Polling
33
+
34
+ ## 📦 Installation
35
+
36
+ ```bash
37
+ npm install @urasoft/live-chat-widget
38
+ ```
39
+
40
+ Or with yarn:
41
+
42
+ ```bash
43
+ yarn add @urasoft/live-chat-widget
44
+ ```
45
+
46
+ Or with pnpm:
47
+
48
+ ```bash
49
+ pnpm add @urasoft/live-chat-widget
50
+ ```
51
+
52
+ ## 🔧 Quick Start
53
+
54
+ ### Basic Usage with SignalR
55
+
56
+ ```typescript
57
+ import { ChatWidget } from "@urasoft/live-chat-widget";
58
+ import "@urasoft/live-chat-widget/dist/styles.css";
59
+
60
+ const chat = new ChatWidget({
61
+ position: "bottom-right",
62
+ primaryColor: "#007bff",
63
+ title: "Live Support",
64
+ signalR: {
65
+ hubUrl: "https://your-server.com/chatHub",
66
+ },
67
+ });
68
+ ```
69
+
70
+ ### With JWT Authentication
71
+
72
+ ```typescript
73
+ const chat = new ChatWidget({
74
+ position: "bottom-right",
75
+ signalR: {
76
+ hubUrl: "/chatHub",
77
+ accessTokenFactory: async () => {
78
+ const response = await fetch("/api/auth/token");
79
+ const data = await response.json();
80
+ return data.token;
81
+ },
82
+ },
83
+ });
84
+ ```
85
+
86
+ ### HTTP + SignalR Hybrid (Send via HTTP, Receive via SignalR)
87
+
88
+ ```typescript
89
+ const chat = new ChatWidget({
90
+ position: "bottom-right",
91
+ primaryColor: "#007bff",
92
+ title: "Live Support",
93
+ // HTTP: Send messages via REST API
94
+ http: {
95
+ sendEndpoint: "https://api.example.com/chat/messages",
96
+ method: "POST",
97
+ headers: {
98
+ "Authorization": "Bearer YOUR_TOKEN",
99
+ "Content-Type": "application/json"
100
+ },
101
+ bodyMapper: (message) => ({
102
+ content: message,
103
+ userId: "user-123",
104
+ timestamp: new Date().toISOString()
105
+ })
106
+ },
107
+ // SignalR: Receive messages in real-time
108
+ signalR: {
109
+ hubUrl: "https://api.example.com/chatHub",
110
+ receiveMethodName: "ReceiveMessage",
111
+ onMessageReceived: (message) => {
112
+ console.log("New message:", message);
113
+ }
114
+ }
115
+ });
116
+ ```
117
+
118
+ ### User Flow
119
+
120
+ **SignalR Only (Default):**
121
+ 1. User clicks the chat bubble
122
+ 2. Clicks "Connect to Live Support" button
123
+ 3. SignalR connection is established automatically
124
+ 4. Real-time messaging begins
125
+ 5. User can disconnect by clicking the disconnect button (🔌)
126
+
127
+ **HTTP + SignalR Hybrid:**
128
+ 1. User clicks the chat bubble (no connection button needed)
129
+ 2. User can immediately start typing
130
+ 3. Messages are sent via HTTP POST to your API
131
+ 4. Responses are received via SignalR in real-time
132
+
133
+ ## 📖 Documentation
134
+
135
+ ### Table of Contents
136
+
137
+ - [Configuration Options](#configuration-options)
138
+ - [SignalR Integration](#signalr-integration)
139
+ - [Reconnection Strategies](#reconnection-strategies)
140
+ - [Event Callbacks](#event-callbacks)
141
+ - [Transport & Logging](#transport--logging)
142
+ - [HTTP Integration](#http-integration)
143
+ - [Basic HTTP Setup](#basic-http-setup)
144
+ - [Custom Body Mapping](#custom-body-mapping)
145
+ - [Dynamic Headers](#dynamic-headers)
146
+ - [HTTP + SignalR Hybrid](#http--signalr-hybrid-1)
147
+ - [Framework Integration](#framework-integration)
148
+ - [React / Next.js](#react--nextjs)
149
+ - [Vue 3](#vue-3)
150
+ - [Angular](#angular)
151
+ - [Vanilla JavaScript](#vanilla-javascript)
152
+ - [API Methods](#api-methods)
153
+ - [Backend Setup](#backend-setup)
154
+
155
+ ---
156
+
157
+ ## Configuration Options
158
+
159
+ ### Complete Configuration Interface
160
+
161
+ ```typescript
162
+ interface ChatConfig {
163
+ // ============================================
164
+ // UI Configuration
165
+ // ============================================
166
+
167
+ /** Widget position on screen
168
+ * @default 'bottom-right'
169
+ */
170
+ position?: "bottom-right" | "bottom-left" | "top-right" | "top-left";
171
+
172
+ /** Primary color for the chat widget
173
+ * @default '#007bff'
174
+ */
175
+ primaryColor?: string;
176
+
177
+ /** Welcome message displayed when chat opens */
178
+ welcomeMessage?: string;
179
+
180
+ /** Chat header title
181
+ * @default 'Live Support'
182
+ */
183
+ title?: string;
184
+
185
+ /** Input placeholder text
186
+ * @default 'Type your message...'
187
+ */
188
+ placeholder?: string;
189
+
190
+ /** Agent/Support team name
191
+ * @default 'Support Team'
192
+ */
193
+ agentName?: string;
194
+
195
+ /** Agent avatar URL */
196
+ agentAvatar?: string;
197
+
198
+ /** Auto-open chat on page load
199
+ * @default false
200
+ */
201
+ autoOpen?: boolean;
202
+
203
+ /** Chat button icon
204
+ * @default '💬'
205
+ */
206
+ buttonIcon?: string;
207
+
208
+ // ============================================
209
+ // SignalR Configuration
210
+ // ============================================
211
+
212
+ signalR?: {
213
+ /** SignalR Hub URL (required) */
214
+ hubUrl: string;
215
+
216
+ /** Query parameters appended to Hub URL */
217
+ queryParams?: Record<string, string>;
218
+
219
+ /** HTTP Headers sent with the connection */
220
+ headers?: Record<string, string>;
221
+
222
+ /** Function to retrieve JWT token for authentication */
223
+ accessTokenFactory?: () => string | Promise<string>;
224
+
225
+ /** Hub method name for sending messages
226
+ * @default 'SendMessage'
227
+ */
228
+ sendMethodName?: string;
229
+
230
+ /** Hub method name for receiving messages
231
+ * @default 'ReceiveMessage'
232
+ */
233
+ receiveMethodName?: string;
234
+
235
+ // ============================================
236
+ // Reconnection Settings
237
+ // ============================================
238
+
239
+ reconnect?: {
240
+ /** Maximum number of reconnection attempts
241
+ * @default 5
242
+ */
243
+ maxRetries?: number;
244
+
245
+ /** Reconnection delay strategy
246
+ * - exponential: 1s, 2s, 4s, 8s, 16s... (default)
247
+ * - linear: 1s, 2s, 3s, 4s, 5s...
248
+ * - fixed: same delay each time
249
+ * - custom function: (retryCount) => delayInMs
250
+ * @default 'exponential'
251
+ */
252
+ delayStrategy?:
253
+ | "exponential"
254
+ | "linear"
255
+ | "fixed"
256
+ | ((retryCount: number) => number);
257
+
258
+ /** Initial delay in milliseconds
259
+ * @default 1000
260
+ */
261
+ initialDelay?: number;
262
+
263
+ /** Maximum delay in milliseconds
264
+ * @default 30000
265
+ */
266
+ maxDelay?: number;
267
+
268
+ /** Fixed delay in milliseconds (when using 'fixed' strategy)
269
+ * @default 5000
270
+ */
271
+ fixedDelay?: number;
272
+
273
+ /** Disable automatic reconnection
274
+ * @default false
275
+ */
276
+ disableAutoReconnect?: boolean;
277
+ };
278
+
279
+ // ============================================
280
+ // Event Callbacks
281
+ // ============================================
282
+
283
+ /** Called when connection is established */
284
+ onConnected?: (connectionId?: string) => void;
285
+
286
+ /** Called when connection is lost */
287
+ onDisconnected?: (error?: Error) => void;
288
+
289
+ /** Called when reconnection attempt starts */
290
+ onReconnecting?: (error?: Error) => void;
291
+
292
+ /** Called when reconnection succeeds */
293
+ onReconnected?: (connectionId?: string) => void;
294
+
295
+ /** Called before each retry attempt */
296
+ onRetry?: (
297
+ retryCount: number,
298
+ delayMs: number,
299
+ elapsedMs?: number
300
+ ) => void;
301
+
302
+ /** Called when all reconnection attempts fail */
303
+ onReconnectFailed?: () => void;
304
+
305
+ /** Called when a message is received */
306
+ onMessageReceived?: (message: any) => void;
307
+
308
+ /** Called before sending a message (can modify message) */
309
+ onBeforeSend?: (message: string) => string | Promise<string>;
310
+
311
+ /** Called after message is sent */
312
+ onAfterSend?: (message: string) => void;
313
+
314
+ /** Called when any error occurs */
315
+ onError?: (error: Error) => void;
316
+
317
+ // ============================================
318
+ // Transport & Logging
319
+ // ============================================
320
+
321
+ /** Transport protocol
322
+ * @default 'All'
323
+ */
324
+ transport?: "WebSockets" | "ServerSentEvents" | "LongPolling" | "All";
325
+
326
+ /** Logging level
327
+ * @default 'Information'
328
+ */
329
+ logLevel?:
330
+ | "Trace"
331
+ | "Debug"
332
+ | "Information"
333
+ | "Warning"
334
+ | "Error"
335
+ | "Critical"
336
+ | "None";
337
+ };
338
+
339
+ // ============================================
340
+ // HTTP Configuration (Optional - for REST API message sending)
341
+ // ============================================
342
+
343
+ http?: {
344
+ /** HTTP Endpoint URL for sending messages (required) */
345
+ sendEndpoint: string;
346
+
347
+ /** HTTP Method
348
+ * @default 'POST'
349
+ */
350
+ method?: "POST" | "PUT" | "PATCH";
351
+
352
+ /** HTTP Headers (static or dynamic function) */
353
+ headers?:
354
+ | Record<string, string>
355
+ | (() => Record<string, string> | Promise<Record<string, string>>);
356
+
357
+ /** Custom body mapper - transform message before sending
358
+ * @example (message) => ({ content: message, userId: '123' })
359
+ */
360
+ bodyMapper?: (message: string, attachments?: FileAttachment[]) => any;
361
+
362
+ /** Response handler (optional) */
363
+ onResponse?: (response: Response) => void | Promise<void>;
364
+
365
+ /** Error handler */
366
+ onError?: (error: Error) => void;
367
+
368
+ /** Request timeout in milliseconds
369
+ * @default 30000
370
+ */
371
+ timeout?: number;
372
+ };
373
+
374
+ /** Callback when message is sent (when NOT using SignalR or HTTP) */
375
+ onSendMessage?: (message: string) => void;
376
+ }
377
+ ```
378
+
379
+ ---
380
+
381
+ ## SignalR Integration
382
+
383
+ ### Basic SignalR Setup
384
+
385
+ The widget automatically manages SignalR connections:
386
+
387
+ ```typescript
388
+ import { ChatWidget } from "@urasoft/live-chat-widget";
389
+ import "@urasoft/live-chat-widget/dist/styles.css";
390
+
391
+ const chat = new ChatWidget({
392
+ signalR: {
393
+ hubUrl: "https://api.example.com/chatHub",
394
+ },
395
+ });
396
+ ```
397
+
398
+ ### Reconnection Strategies
399
+
400
+ The widget supports multiple reconnection strategies when connection is lost.
401
+
402
+ #### 1. Exponential Backoff (Default)
403
+
404
+ Delay doubles with each attempt: 1s → 2s → 4s → 8s → 16s...
405
+
406
+ ```typescript
407
+ const chat = new ChatWidget({
408
+ signalR: {
409
+ hubUrl: "/chatHub",
410
+ reconnect: {
411
+ maxRetries: 10,
412
+ delayStrategy: "exponential",
413
+ initialDelay: 1000, // Start with 1 second
414
+ maxDelay: 60000, // Cap at 60 seconds
415
+ },
416
+ onRetry: (retryCount, delayMs) => {
417
+ console.log(`🔄 Retry attempt ${retryCount}, waiting ${delayMs}ms`);
418
+ },
419
+ onReconnected: (connectionId) => {
420
+ console.log("✅ Reconnected:", connectionId);
421
+ },
422
+ onReconnectFailed: () => {
423
+ console.error("❌ All reconnection attempts failed");
424
+ alert("Unable to connect. Please refresh the page.");
425
+ },
426
+ },
427
+ });
428
+ ```
429
+
430
+ #### 2. Linear Backoff
431
+
432
+ Delay increases linearly: 1s → 2s → 3s → 4s → 5s...
433
+
434
+ ```typescript
435
+ const chat = new ChatWidget({
436
+ signalR: {
437
+ hubUrl: "/chatHub",
438
+ reconnect: {
439
+ maxRetries: 5,
440
+ delayStrategy: "linear",
441
+ initialDelay: 2000, // Start with 2 seconds
442
+ },
443
+ },
444
+ });
445
+ ```
446
+
447
+ #### 3. Fixed Delay
448
+
449
+ Same delay for each attempt: 5s → 5s → 5s → 5s...
450
+
451
+ ```typescript
452
+ const chat = new ChatWidget({
453
+ signalR: {
454
+ hubUrl: "/chatHub",
455
+ reconnect: {
456
+ maxRetries: 8,
457
+ delayStrategy: "fixed",
458
+ fixedDelay: 5000, // Wait 5 seconds between each attempt
459
+ },
460
+ },
461
+ });
462
+ ```
463
+
464
+ #### 4. Custom Delay Function
465
+
466
+ Define your own reconnection strategy:
467
+
468
+ ```typescript
469
+ const chat = new ChatWidget({
470
+ signalR: {
471
+ hubUrl: "/chatHub",
472
+ reconnect: {
473
+ maxRetries: 10,
474
+ delayStrategy: (retryCount) => {
475
+ // Custom strategy: Fibonacci-like delays
476
+ if (retryCount === 0) return 1000;
477
+ if (retryCount === 1) return 1000;
478
+ return Math.min(
479
+ fibonacci(retryCount) * 1000,
480
+ 60000 // Cap at 60 seconds
481
+ );
482
+ },
483
+ },
484
+ },
485
+ });
486
+
487
+ function fibonacci(n: number): number {
488
+ if (n <= 1) return 1;
489
+ return fibonacci(n - 1) + fibonacci(n - 2);
490
+ }
491
+ ```
492
+
493
+ ### Event Callbacks
494
+
495
+ Monitor every stage of the SignalR connection lifecycle:
496
+
497
+ ```typescript
498
+ const chat = new ChatWidget({
499
+ signalR: {
500
+ hubUrl: "/chatHub",
501
+
502
+ // Connection established
503
+ onConnected: (connectionId) => {
504
+ console.log("✅ Connected, Connection ID:", connectionId);
505
+ analytics.track("chat_connected");
506
+ },
507
+
508
+ // Connection lost
509
+ onDisconnected: (error) => {
510
+ console.log("❌ Disconnected:", error?.message);
511
+ analytics.track("chat_disconnected");
512
+ },
513
+
514
+ // Reconnection started
515
+ onReconnecting: (error) => {
516
+ console.log("🔄 Reconnecting...", error?.message);
517
+ // Show "Connecting..." in UI
518
+ },
519
+
520
+ // Reconnection succeeded
521
+ onReconnected: (connectionId) => {
522
+ console.log("✅ Reconnected:", connectionId);
523
+ // Show "Connected" in UI
524
+ },
525
+
526
+ // Before each retry attempt
527
+ onRetry: (retryCount, delayMs, elapsedMs) => {
528
+ console.log(`🔄 Attempt ${retryCount}, will wait ${delayMs}ms`);
529
+ console.log(`⏱️ Total elapsed time: ${elapsedMs}ms`);
530
+ // Update progress bar
531
+ },
532
+
533
+ // All retry attempts exhausted
534
+ onReconnectFailed: () => {
535
+ console.error("❌ Reconnection failed");
536
+ alert("Cannot connect to server. Please refresh the page.");
537
+ },
538
+
539
+ // Message received
540
+ onMessageReceived: (message) => {
541
+ console.log("📩 Message received:", message);
542
+ playNotificationSound();
543
+ },
544
+
545
+ // Before sending message (can modify it)
546
+ onBeforeSend: async (message) => {
547
+ console.log("📤 Sending:", message);
548
+ // Encrypt, sanitize, or format the message
549
+ const sanitized = sanitizeMessage(message);
550
+ return sanitized;
551
+ },
552
+
553
+ // After message sent
554
+ onAfterSend: (message) => {
555
+ console.log("✅ Sent:", message);
556
+ analytics.track("message_sent", { length: message.length });
557
+ },
558
+
559
+ // Any error occurs
560
+ onError: (error) => {
561
+ console.error("⚠️ SignalR Error:", error.message);
562
+ // Send to error tracking service (Sentry, LogRocket, etc.)
563
+ errorReporter.captureException(error);
564
+ },
565
+
566
+ reconnect: {
567
+ maxRetries: 5,
568
+ delayStrategy: "exponential",
569
+ },
570
+ },
571
+ });
572
+ ```
573
+
574
+ ### Transport & Logging
575
+
576
+ Configure transport protocol and logging level:
577
+
578
+ ```typescript
579
+ const chat = new ChatWidget({
580
+ signalR: {
581
+ hubUrl: "/chatHub",
582
+
583
+ // Transport type
584
+ // Options: 'WebSockets' | 'ServerSentEvents' | 'LongPolling' | 'All'
585
+ transport: "WebSockets",
586
+
587
+ // Log level
588
+ // Use 'Debug' for development, 'Warning' or 'Error' for production
589
+ // Options: 'Trace' | 'Debug' | 'Information' | 'Warning' | 'Error' | 'Critical' | 'None'
590
+ logLevel: "Debug",
591
+ },
592
+ });
593
+ ```
594
+
595
+ ### JWT Authentication
596
+
597
+ Secure your connection with JWT tokens:
598
+
599
+ ```typescript
600
+ const chat = new ChatWidget({
601
+ signalR: {
602
+ hubUrl: "/chatHub",
603
+ accessTokenFactory: async () => {
604
+ const response = await fetch("/api/auth/token");
605
+ const { token } = await response.json();
606
+ return token;
607
+ },
608
+ },
609
+ });
610
+ ```
611
+
612
+ ### Query Parameters
613
+
614
+ Pass user information via query parameters:
615
+
616
+ ```typescript
617
+ const chat = new ChatWidget({
618
+ signalR: {
619
+ hubUrl: "/chatHub",
620
+ queryParams: {
621
+ userId: currentUser.id,
622
+ userName: currentUser.name,
623
+ department: "support",
624
+ language: "en",
625
+ },
626
+ },
627
+ });
628
+ ```
629
+
630
+ ### Custom Headers
631
+
632
+ Add custom HTTP headers to the connection:
633
+
634
+ ```typescript
635
+ const chat = new ChatWidget({
636
+ signalR: {
637
+ hubUrl: "/chatHub",
638
+ headers: {
639
+ "X-Api-Key": "your-api-key",
640
+ "X-Client-Version": "1.0.0",
641
+ "X-Device-Id": deviceId,
642
+ },
643
+ },
644
+ });
645
+ ```
646
+
647
+ ---
648
+
649
+ ## HTTP Integration
650
+
651
+ The widget supports sending messages via HTTP REST API while optionally receiving messages through SignalR. This hybrid approach is perfect for scenarios where you want to use your existing REST API for message submission.
652
+
653
+ ### Basic HTTP Setup
654
+
655
+ Send messages via HTTP POST to your API endpoint:
656
+
657
+ ```typescript
658
+ import { ChatWidget } from "@urasoft/live-chat-widget";
659
+ import "@urasoft/live-chat-widget/dist/styles.css";
660
+
661
+ const chat = new ChatWidget({
662
+ position: "bottom-right",
663
+ primaryColor: "#007bff",
664
+ title: "Support Chat",
665
+ http: {
666
+ sendEndpoint: "https://api.example.com/chat/messages",
667
+ method: "POST",
668
+ headers: {
669
+ "Content-Type": "application/json",
670
+ "Authorization": "Bearer YOUR_API_TOKEN"
671
+ }
672
+ }
673
+ });
674
+ ```
675
+
676
+ **Default Request Body Format:**
677
+ ```json
678
+ {
679
+ "message": "User's message text",
680
+ "attachments": [],
681
+ "timestamp": "2025-01-15T10:30:00.000Z"
682
+ }
683
+ ```
684
+
685
+ ### Custom Body Mapping
686
+
687
+ Transform the message into your API's expected format:
688
+
689
+ ```typescript
690
+ const chat = new ChatWidget({
691
+ http: {
692
+ sendEndpoint: "https://api.example.com/messages",
693
+ bodyMapper: (message, attachments) => ({
694
+ // Custom format for your API
695
+ content: message,
696
+ userId: getCurrentUserId(),
697
+ sessionId: getSessionId(),
698
+ files: attachments?.map(f => ({
699
+ filename: f.name,
700
+ base64: f.data,
701
+ mimeType: f.type
702
+ })),
703
+ metadata: {
704
+ timestamp: new Date().toISOString(),
705
+ platform: "web",
706
+ language: navigator.language
707
+ }
708
+ })
709
+ }
710
+ });
711
+ ```
712
+
713
+ ### Dynamic Headers
714
+
715
+ Use a function to provide dynamic headers (e.g., fresh JWT tokens):
716
+
717
+ ```typescript
718
+ const chat = new ChatWidget({
719
+ http: {
720
+ sendEndpoint: "/api/chat/send",
721
+ headers: async () => {
722
+ // Fetch fresh token on every request
723
+ const token = await getAccessToken();
724
+ return {
725
+ "Content-Type": "application/json",
726
+ "Authorization": `Bearer ${token}`,
727
+ "X-Request-ID": generateRequestId(),
728
+ "X-User-ID": getCurrentUserId()
729
+ };
730
+ },
731
+ bodyMapper: (message) => ({
732
+ text: message,
733
+ userId: getCurrentUserId()
734
+ })
735
+ }
736
+ });
737
+ ```
738
+
739
+ ### HTTP + SignalR Hybrid
740
+
741
+ **Perfect use case:** Send messages via your REST API, receive responses via SignalR
742
+
743
+ ```typescript
744
+ const chat = new ChatWidget({
745
+ position: "bottom-right",
746
+
747
+ // HTTP: Send messages to REST API
748
+ http: {
749
+ sendEndpoint: "https://api.example.com/chat/messages",
750
+ method: "POST",
751
+ headers: async () => ({
752
+ "Authorization": `Bearer ${await getToken()}`,
753
+ "Content-Type": "application/json"
754
+ }),
755
+ bodyMapper: (message) => ({
756
+ content: message,
757
+ userId: currentUser.id,
758
+ conversationId: conversationId
759
+ }),
760
+ onResponse: async (response) => {
761
+ const data = await response.json();
762
+ console.log("Message sent successfully:", data.messageId);
763
+ },
764
+ onError: (error) => {
765
+ console.error("Failed to send message:", error);
766
+ alert("Could not send message. Please try again.");
767
+ },
768
+ timeout: 10000 // 10 seconds
769
+ },
770
+
771
+ // SignalR: Receive messages in real-time
772
+ signalR: {
773
+ hubUrl: "https://api.example.com/chatHub",
774
+ queryParams: {
775
+ userId: currentUser.id,
776
+ conversationId: conversationId
777
+ },
778
+ receiveMethodName: "ReceiveMessage",
779
+ onConnected: (connectionId) => {
780
+ console.log("Connected to SignalR:", connectionId);
781
+ },
782
+ onMessageReceived: (message) => {
783
+ console.log("New message received:", message);
784
+ },
785
+ reconnect: {
786
+ maxRetries: 5,
787
+ delayStrategy: "exponential"
788
+ }
789
+ }
790
+ });
791
+ ```
792
+
793
+ ### Error Handling & Response Processing
794
+
795
+ ```typescript
796
+ const chat = new ChatWidget({
797
+ http: {
798
+ sendEndpoint: "/api/chat/messages",
799
+
800
+ // Handle successful responses
801
+ onResponse: async (response) => {
802
+ const data = await response.json();
803
+
804
+ // Track analytics
805
+ analytics.track("message_sent", {
806
+ messageId: data.id,
807
+ timestamp: data.timestamp
808
+ });
809
+
810
+ // Show confirmation toast
811
+ if (data.queued) {
812
+ showToast("Your message is being processed...");
813
+ }
814
+ },
815
+
816
+ // Handle errors
817
+ onError: (error) => {
818
+ // Log to error tracking service
819
+ errorTracker.captureException(error);
820
+
821
+ // Show user-friendly message
822
+ if (error.name === "AbortError") {
823
+ showToast("Request timed out. Please try again.");
824
+ } else if (error.message.includes("401")) {
825
+ showToast("Session expired. Please log in again.");
826
+ redirectToLogin();
827
+ } else {
828
+ showToast("Failed to send message. Please check your connection.");
829
+ }
830
+ },
831
+
832
+ timeout: 15000 // 15 seconds
833
+ }
834
+ });
835
+ ```
836
+
837
+ ### HTTP Only (No SignalR)
838
+
839
+ If you don't need real-time message receiving:
840
+
841
+ ```typescript
842
+ const chat = new ChatWidget({
843
+ position: "bottom-right",
844
+ title: "Contact Us",
845
+ welcomeMessage: "Send us a message and we'll get back to you soon!",
846
+
847
+ http: {
848
+ sendEndpoint: "https://api.example.com/contact/submit",
849
+ bodyMapper: (message) => ({
850
+ message: message,
851
+ email: userEmail,
852
+ subject: "Website Inquiry",
853
+ timestamp: new Date().toISOString()
854
+ }),
855
+ onResponse: async (response) => {
856
+ const data = await response.json();
857
+ // Show success message in chat
858
+ chat.addAgentMessage(
859
+ `Thank you! Your message has been received. Reference: ${data.ticketId}`
860
+ );
861
+ }
862
+ }
863
+ });
864
+ ```
865
+
866
+ ### Backend API Example (Node.js/Express)
867
+
868
+ ```javascript
869
+ app.post("/api/chat/messages", async (req, res) => {
870
+ const { content, userId, conversationId } = req.body;
871
+
872
+ // Save message to database
873
+ const message = await db.messages.create({
874
+ content,
875
+ userId,
876
+ conversationId,
877
+ timestamp: new Date()
878
+ });
879
+
880
+ // Process message (AI, routing, etc.)
881
+ const response = await processMessage(message);
882
+
883
+ // Send response via SignalR to user
884
+ await signalRHub.clients.user(userId).send("ReceiveMessage", {
885
+ text: response.content,
886
+ avatar: response.agentAvatar,
887
+ timestamp: new Date()
888
+ });
889
+
890
+ // Return confirmation
891
+ res.json({
892
+ success: true,
893
+ messageId: message.id,
894
+ queued: true
895
+ });
896
+ });
897
+ ```
898
+
899
+ ---
900
+
901
+ ## Framework Integration
902
+
903
+ ### React / Next.js
904
+
905
+ ```tsx
906
+ "use client"; // For Next.js 13+ App Router
907
+
908
+ import { useEffect, useRef } from "react";
909
+ import { ChatWidget } from "@urasoft/live-chat-widget";
910
+ import "@urasoft/live-chat-widget/dist/styles.css";
911
+
912
+ export default function ChatComponent() {
913
+ const chatRef = useRef<ChatWidget | null>(null);
914
+
915
+ useEffect(() => {
916
+ chatRef.current = new ChatWidget({
917
+ position: "bottom-right",
918
+ primaryColor: "#007bff",
919
+ title: "Live Support",
920
+ signalR: {
921
+ hubUrl: process.env.NEXT_PUBLIC_CHAT_HUB_URL || "/chatHub",
922
+ queryParams: {
923
+ userId: "user-123",
924
+ },
925
+ accessTokenFactory: async () => {
926
+ const response = await fetch("/api/auth/token");
927
+ const data = await response.json();
928
+ return data.token;
929
+ },
930
+ onConnected: (connectionId) => {
931
+ console.log("Chat connected:", connectionId);
932
+ },
933
+ onMessageReceived: (message) => {
934
+ console.log("New message:", message);
935
+ },
936
+ },
937
+ });
938
+
939
+ return () => {
940
+ chatRef.current?.destroy();
941
+ };
942
+ }, []);
943
+
944
+ return null;
945
+ }
946
+ ```
947
+
948
+ ### Vue 3
949
+
950
+ ```vue
951
+ <template>
952
+ <div></div>
953
+ </template>
954
+
955
+ <script setup lang="ts">
956
+ import { onMounted, onUnmounted } from "vue";
957
+ import { ChatWidget } from "@urasoft/live-chat-widget";
958
+ import "@urasoft/live-chat-widget/dist/styles.css";
959
+
960
+ let chat: ChatWidget | null = null;
961
+
962
+ onMounted(() => {
963
+ chat = new ChatWidget({
964
+ position: "bottom-right",
965
+ primaryColor: "#42b883",
966
+ title: "Live Support",
967
+ signalR: {
968
+ hubUrl: import.meta.env.VITE_CHAT_HUB_URL || "/chatHub",
969
+ queryParams: {
970
+ sessionId: "vue-session-123",
971
+ },
972
+ onConnected: (connectionId) => {
973
+ console.log("Chat connected:", connectionId);
974
+ },
975
+ onMessageReceived: (message) => {
976
+ console.log("New message:", message);
977
+ },
978
+ },
979
+ });
980
+ });
981
+
982
+ onUnmounted(() => {
983
+ chat?.destroy();
984
+ });
985
+ </script>
986
+ ```
987
+
988
+ ### Angular
989
+
990
+ ```typescript
991
+ import { Component, OnInit, OnDestroy } from "@angular/core";
992
+ import { ChatWidget } from "@urasoft/live-chat-widget";
993
+ import "@urasoft/live-chat-widget/dist/styles.css";
994
+
995
+ @Component({
996
+ selector: "app-chat",
997
+ template: "",
998
+ })
999
+ export class ChatComponent implements OnInit, OnDestroy {
1000
+ private chat: ChatWidget | null = null;
1001
+
1002
+ ngOnInit() {
1003
+ this.chat = new ChatWidget({
1004
+ position: "bottom-right",
1005
+ primaryColor: "#dd0031",
1006
+ title: "Live Support",
1007
+ signalR: {
1008
+ hubUrl: "/chatHub",
1009
+ queryParams: {
1010
+ userId: "angular-user-123",
1011
+ },
1012
+ onConnected: (connectionId) => {
1013
+ console.log("Chat connected:", connectionId);
1014
+ },
1015
+ onMessageReceived: (message) => {
1016
+ console.log("New message:", message);
1017
+ },
1018
+ },
1019
+ });
1020
+ }
1021
+
1022
+ ngOnDestroy() {
1023
+ this.chat?.destroy();
1024
+ }
1025
+ }
1026
+ ```
1027
+
1028
+ ### Vanilla JavaScript
1029
+
1030
+ ```html
1031
+ <!DOCTYPE html>
1032
+ <html lang="en">
1033
+ <head>
1034
+ <meta charset="UTF-8" />
1035
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
1036
+ <title>Chat Widget Demo</title>
1037
+ <link
1038
+ rel="stylesheet"
1039
+ href="node_modules/@urasoft/live-chat-widget/dist/styles.css"
1040
+ />
1041
+ </head>
1042
+ <body>
1043
+ <h1>My Website</h1>
1044
+
1045
+ <script type="module">
1046
+ import { ChatWidget } from "./node_modules/@urasoft/live-chat-widget/dist/index.esm.js";
1047
+
1048
+ const chat = new ChatWidget({
1049
+ position: "bottom-right",
1050
+ primaryColor: "#007bff",
1051
+ title: "Live Support",
1052
+ welcomeMessage: "Hello! How can we help you today?",
1053
+ signalR: {
1054
+ hubUrl: "https://your-server.com/chatHub",
1055
+ queryParams: {
1056
+ userId: "guest-" + Date.now(),
1057
+ },
1058
+ onConnected: () => {
1059
+ console.log("Connected to live chat");
1060
+ },
1061
+ onMessageReceived: (message) => {
1062
+ console.log("Received:", message);
1063
+ },
1064
+ },
1065
+ });
1066
+ </script>
1067
+ </body>
1068
+ </html>
1069
+ ```
1070
+
1071
+ ---
1072
+
1073
+ ## API Methods
1074
+
1075
+ ### `open()`
1076
+
1077
+ Open the chat window programmatically.
1078
+
1079
+ ```typescript
1080
+ chat.open();
1081
+ ```
1082
+
1083
+ ### `close()`
1084
+
1085
+ Close the chat window programmatically.
1086
+
1087
+ ```typescript
1088
+ chat.close();
1089
+ ```
1090
+
1091
+ ### `destroy()`
1092
+
1093
+ Completely remove the widget from the page and disconnect SignalR.
1094
+
1095
+ ```typescript
1096
+ chat.destroy();
1097
+ ```
1098
+
1099
+ ### `addAgentMessage(message, avatar?)`
1100
+
1101
+ Add an agent message programmatically (useful for SignalR message handling).
1102
+
1103
+ ```typescript
1104
+ chat.addAgentMessage("Hello! How can I help you?", "/avatar.png");
1105
+ ```
1106
+
1107
+ ### `isConnected()`
1108
+
1109
+ Check if SignalR is currently connected.
1110
+
1111
+ ```typescript
1112
+ if (chat.isConnected()) {
1113
+ console.log("Chat is connected");
1114
+ }
1115
+ ```
1116
+
1117
+ ---
1118
+
1119
+ ## Backend Setup
1120
+
1121
+ ### ASP.NET Core SignalR Hub (C#)
1122
+
1123
+ ```csharp
1124
+ using Microsoft.AspNetCore.SignalR;
1125
+
1126
+ public class ChatHub : Hub
1127
+ {
1128
+ public async Task SendMessage(string message)
1129
+ {
1130
+ // Get user identifier
1131
+ var userId = Context.UserIdentifier;
1132
+ var connectionId = Context.ConnectionId;
1133
+
1134
+ Console.WriteLine($"Message from {userId}: {message}");
1135
+
1136
+ // Process message and generate response
1137
+ var response = await ProcessMessageAsync(message, userId);
1138
+
1139
+ // Send response back to caller
1140
+ await Clients.Caller.SendAsync("ReceiveMessage", response);
1141
+
1142
+ // Or broadcast to all clients
1143
+ // await Clients.All.SendAsync("ReceiveMessage", response);
1144
+ }
1145
+
1146
+ public override async Task OnConnectedAsync()
1147
+ {
1148
+ // Get query parameters
1149
+ var httpContext = Context.GetHttpContext();
1150
+ var userId = httpContext?.Request.Query["userId"];
1151
+ var userName = httpContext?.Request.Query["userName"];
1152
+
1153
+ Console.WriteLine($"User connected: {userId} ({userName})");
1154
+
1155
+ // Send welcome message
1156
+ await Clients.Caller.SendAsync("ReceiveMessage",
1157
+ "Welcome to live support! How can we help you today?");
1158
+
1159
+ await base.OnConnectedAsync();
1160
+ }
1161
+
1162
+ public override async Task OnDisconnectedAsync(Exception? exception)
1163
+ {
1164
+ Console.WriteLine($"User disconnected: {Context.ConnectionId}");
1165
+
1166
+ if (exception != null)
1167
+ {
1168
+ Console.WriteLine($"Disconnection error: {exception.Message}");
1169
+ }
1170
+
1171
+ await base.OnDisconnectedAsync(exception);
1172
+ }
1173
+
1174
+ private async Task<string> ProcessMessageAsync(string message, string userId)
1175
+ {
1176
+ // Add your message processing logic here
1177
+ // e.g., AI chatbot, database queries, etc.
1178
+
1179
+ await Task.Delay(500); // Simulate processing
1180
+
1181
+ return $"You said: {message}. How else can I assist you?";
1182
+ }
1183
+ }
1184
+ ```
1185
+
1186
+ ### Configure SignalR in Startup/Program.cs
1187
+
1188
+ ```csharp
1189
+ var builder = WebApplication.CreateBuilder(args);
1190
+
1191
+ // Add SignalR
1192
+ builder.Services.AddSignalR();
1193
+
1194
+ // Add CORS if needed
1195
+ builder.Services.AddCors(options =>
1196
+ {
1197
+ options.AddPolicy("ChatPolicy", policy =>
1198
+ {
1199
+ policy.WithOrigins("http://localhost:3000", "https://yourdomain.com")
1200
+ .AllowAnyHeader()
1201
+ .AllowAnyMethod()
1202
+ .AllowCredentials();
1203
+ });
1204
+ });
1205
+
1206
+ var app = builder.Build();
1207
+
1208
+ app.UseCors("ChatPolicy");
1209
+
1210
+ // Map SignalR hub
1211
+ app.MapHub<ChatHub>("/chatHub");
1212
+
1213
+ app.Run();
1214
+ ```
1215
+
1216
+ ### Node.js / Express Example
1217
+
1218
+ ```javascript
1219
+ const express = require("express");
1220
+ const http = require("http");
1221
+ const { Server } = require("socket.io");
1222
+
1223
+ const app = express();
1224
+ const server = http.createServer(app);
1225
+ const io = new Server(server, {
1226
+ cors: {
1227
+ origin: "http://localhost:3000",
1228
+ methods: ["GET", "POST"],
1229
+ },
1230
+ });
1231
+
1232
+ io.on("connection", (socket) => {
1233
+ console.log("User connected:", socket.id);
1234
+
1235
+ // Get query parameters
1236
+ const { userId, userName } = socket.handshake.query;
1237
+ console.log(`User: ${userName} (${userId})`);
1238
+
1239
+ // Send welcome message
1240
+ socket.emit("ReceiveMessage", "Welcome to live support!");
1241
+
1242
+ // Handle incoming messages
1243
+ socket.on("SendMessage", (message) => {
1244
+ console.log("Message received:", message);
1245
+
1246
+ // Process and respond
1247
+ const response = `You said: ${message}`;
1248
+ socket.emit("ReceiveMessage", response);
1249
+ });
1250
+
1251
+ socket.on("disconnect", () => {
1252
+ console.log("User disconnected:", socket.id);
1253
+ });
1254
+ });
1255
+
1256
+ server.listen(3001, () => {
1257
+ console.log("SignalR server running on port 3001");
1258
+ });
1259
+ ```
1260
+
1261
+ ---
1262
+
1263
+ ## Custom Styling
1264
+
1265
+ ### Using CSS Variables
1266
+
1267
+ ```css
1268
+ :root {
1269
+ --chat-primary-color: #007bff;
1270
+ --chat-secondary-color: #6c757d;
1271
+ --chat-background: #ffffff;
1272
+ --chat-text-color: #212529;
1273
+ }
1274
+ ```
1275
+
1276
+ ### Overriding Tailwind Classes
1277
+
1278
+ ```css
1279
+ /* Override specific chat widget styles */
1280
+ .chat-widget-container {
1281
+ /* Your custom styles */
1282
+ }
1283
+
1284
+ .chat-header {
1285
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
1286
+ }
1287
+
1288
+ .chat-message {
1289
+ border-radius: 12px;
1290
+ }
1291
+ ```
1292
+
1293
+ ---
1294
+
1295
+ ## Troubleshooting
1296
+
1297
+ ### Connection Issues
1298
+
1299
+ **Problem:** Chat won't connect to SignalR Hub
1300
+
1301
+ **Solutions:**
1302
+
1303
+ - Verify `hubUrl` is correct and accessible
1304
+ - Check CORS settings on your server
1305
+ - Ensure SignalR Hub is properly configured
1306
+ - Check browser console for error messages
1307
+ - Verify `accessTokenFactory` returns valid token (if using authentication)
1308
+
1309
+ ### Reconnection Not Working
1310
+
1311
+ **Problem:** Chat doesn't reconnect after connection loss
1312
+
1313
+ **Solutions:**
1314
+
1315
+ - Check `disableAutoReconnect` is not set to `true`
1316
+ - Verify `maxRetries` is greater than 0
1317
+ - Check `onError` callback for error details
1318
+ - Ensure network connectivity is restored
1319
+
1320
+ ### Messages Not Displaying
1321
+
1322
+ **Problem:** Messages sent but not appearing in chat
1323
+
1324
+ **Solutions:**
1325
+
1326
+ - Verify `receiveMethodName` matches server-side method
1327
+ - Check `onMessageReceived` callback is firing
1328
+ - Ensure message format matches expected structure
1329
+ - Check browser console for errors
1330
+
1331
+ ---
1332
+
1333
+ ## Performance Tips
1334
+
1335
+ 1. **Use WebSockets transport** for best performance:
1336
+
1337
+ ```typescript
1338
+ signalR: {
1339
+ transport: "WebSockets";
1340
+ }
1341
+ ```
1342
+
1343
+ 2. **Adjust log level in production**:
1344
+
1345
+ ```typescript
1346
+ signalR: {
1347
+ logLevel: "Warning"; // or "Error"
1348
+ }
1349
+ ```
1350
+
1351
+ 3. **Implement message throttling** in `onBeforeSend`:
1352
+
1353
+ ```typescript
1354
+ onBeforeSend: async (message) => {
1355
+ await throttle(1000); // Wait 1 second between messages
1356
+ return message;
1357
+ };
1358
+ ```
1359
+
1360
+ 4. **Clean up properly** when component unmounts:
1361
+ ```typescript
1362
+ useEffect(() => {
1363
+ const chat = new ChatWidget({...});
1364
+ return () => chat.destroy(); // Important!
1365
+ }, []);
1366
+ ```
1367
+
1368
+ ---
1369
+
1370
+ ## Browser Support
1371
+
1372
+ - ✅ Chrome/Edge (latest)
1373
+ - ✅ Firefox (latest)
1374
+ - ✅ Safari (latest)
1375
+ - ✅ Opera (latest)
1376
+ - ✅ Mobile browsers (iOS Safari, Chrome Mobile)
1377
+
1378
+ ---
1379
+
1380
+ ## Contributing
1381
+
1382
+ Contributions are welcome! Please feel free to submit issues or pull requests.
1383
+
1384
+ 1. Fork the repository
1385
+ 2. Create your feature branch (`git checkout -b feature/amazing-feature`)
1386
+ 3. Commit your changes (`git commit -m 'Add amazing feature'`)
1387
+ 4. Push to the branch (`git push origin feature/amazing-feature`)
1388
+ 5. Open a Pull Request
1389
+
1390
+ ---
1391
+
1392
+ ## License
1393
+
1394
+ MIT © [Urasoft](https://urasoft.com)
1395
+
1396
+ ---
1397
+
1398
+ ## Links
1399
+
1400
+ - **Documentation:** [GitHub Repository](https://github.com/UrasoftYazilim/live-chat-widget)
1401
+ - **npm Package:** [npmjs.com](https://www.npmjs.com/package/@urasoft/live-chat-widget)
1402
+ - **Issues:** [GitHub Issues](https://github.com/UrasoftYazilim/live-chat-widget/issues)
1403
+ - **Contact:** [Urasoft](https://urasoft.com)
1404
+
1405
+ ---
1406
+
1407
+ <div align="center">
1408
+
1409
+ Made with ❤️ by [Urasoft](https://urasoft.com)
1410
+
1411
+ **[⬆ back to top](#urasoft-live-chat-widget)**
1412
+
1413
+ </div>