@blumessage/react-chat 1.2.1 → 1.4.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 +185 -1
- package/dist/BlumessageChat.js +147 -4
- package/dist/blumessage-chat.browser.js +2 -0
- package/dist/blumessage-chat.browser.js.LICENSE.txt +16 -0
- package/dist/index.browser.js +5 -0
- package/dist/index.commonjs.js +8 -0
- package/dist/index.js +2 -1
- package/dist/index.js.LICENSE.txt +46 -0
- package/dist/types/BlumessageChat.d.ts +9 -1
- package/dist/types/index.browser.d.ts +4 -0
- package/dist/types/index.commonjs.d.ts +6 -0
- package/dist/types/index.d.ts +1 -1
- package/package.json +14 -2
package/README.md
CHANGED
|
@@ -15,10 +15,38 @@ A React TypeScript chat widget component with floating button, theming, and Blum
|
|
|
15
15
|
|
|
16
16
|
## Installation
|
|
17
17
|
|
|
18
|
+
### NPM Package (Recommended)
|
|
19
|
+
|
|
18
20
|
```bash
|
|
19
21
|
npm i @blumessage/react-chat
|
|
20
22
|
```
|
|
21
23
|
|
|
24
|
+
### Browser Versions
|
|
25
|
+
|
|
26
|
+
#### Option 1: Standalone (Includes React)
|
|
27
|
+
|
|
28
|
+
For the simplest setup with everything included:
|
|
29
|
+
|
|
30
|
+
```html
|
|
31
|
+
<!-- Load the Blumessage Chat component (includes React) -->
|
|
32
|
+
<script src="path/to/index.js"></script>
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
#### Option 2: External React (Smaller Bundle)
|
|
36
|
+
|
|
37
|
+
For when you already have React loaded:
|
|
38
|
+
|
|
39
|
+
```html
|
|
40
|
+
<!-- Load React and ReactDOM from CDN -->
|
|
41
|
+
<script crossorigin src="https://unpkg.com/react@18/umd/react.production.min.js"></script>
|
|
42
|
+
<script crossorigin src="https://unpkg.com/react-dom@18/umd/react-dom.production.min.js"></script>
|
|
43
|
+
|
|
44
|
+
<!-- Load the Blumessage Chat component -->
|
|
45
|
+
<script src="path/to/blumessage-chat.browser.js"></script>
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
See [BROWSER_USAGE.md](./BROWSER_USAGE.md) for complete browser usage documentation.
|
|
49
|
+
|
|
22
50
|
## Quick Start
|
|
23
51
|
|
|
24
52
|
### Floating Chat Widget (Default)
|
|
@@ -273,12 +301,133 @@ The `onError` callback provides detailed error context:
|
|
|
273
301
|
/>
|
|
274
302
|
```
|
|
275
303
|
|
|
304
|
+
## External Message Submission
|
|
305
|
+
|
|
306
|
+
The component supports external message submission through a ref, allowing parent components to programmatically send messages and control the chat widget.
|
|
307
|
+
|
|
308
|
+
### Using the Ref
|
|
309
|
+
|
|
310
|
+
```tsx
|
|
311
|
+
import React, { useRef } from 'react';
|
|
312
|
+
import { BlumessageChat, BlumessageChatRef } from '@blumessage/react-chat';
|
|
313
|
+
|
|
314
|
+
function App() {
|
|
315
|
+
const chatRef = useRef<BlumessageChatRef>(null);
|
|
316
|
+
|
|
317
|
+
const sendExternalMessage = async () => {
|
|
318
|
+
if (chatRef.current) {
|
|
319
|
+
await chatRef.current.sendMessage("Hello from external component!");
|
|
320
|
+
}
|
|
321
|
+
};
|
|
322
|
+
|
|
323
|
+
const openChat = () => {
|
|
324
|
+
chatRef.current?.openChat();
|
|
325
|
+
};
|
|
326
|
+
|
|
327
|
+
const closeChat = () => {
|
|
328
|
+
chatRef.current?.closeChat();
|
|
329
|
+
};
|
|
330
|
+
|
|
331
|
+
const clearChat = () => {
|
|
332
|
+
chatRef.current?.clearConversation();
|
|
333
|
+
};
|
|
334
|
+
|
|
335
|
+
const getMessages = () => {
|
|
336
|
+
const messages = chatRef.current?.getMessages();
|
|
337
|
+
console.log('Current messages:', messages);
|
|
338
|
+
};
|
|
339
|
+
|
|
340
|
+
const getToken = () => {
|
|
341
|
+
const token = chatRef.current?.getToken();
|
|
342
|
+
console.log('Current conversation token:', token);
|
|
343
|
+
};
|
|
344
|
+
|
|
345
|
+
return (
|
|
346
|
+
<div>
|
|
347
|
+
<button onClick={sendExternalMessage}>Send External Message</button>
|
|
348
|
+
<button onClick={openChat}>Open Chat</button>
|
|
349
|
+
<button onClick={closeChat}>Close Chat</button>
|
|
350
|
+
<button onClick={clearChat}>Clear Chat</button>
|
|
351
|
+
<button onClick={getMessages}>Get Messages</button>
|
|
352
|
+
<button onClick={getToken}>Get Token</button>
|
|
353
|
+
|
|
354
|
+
<BlumessageChat
|
|
355
|
+
ref={chatRef}
|
|
356
|
+
apiKey="your-api-key"
|
|
357
|
+
floating={true}
|
|
358
|
+
/>
|
|
359
|
+
</div>
|
|
360
|
+
);
|
|
361
|
+
}
|
|
362
|
+
```
|
|
363
|
+
|
|
364
|
+
### Available Ref Methods
|
|
365
|
+
|
|
366
|
+
| Method | Type | Description |
|
|
367
|
+
|--------|------|-------------|
|
|
368
|
+
| `sendMessage(message: string)` | `Promise<void>` | Send a message programmatically |
|
|
369
|
+
| `openChat()` | `void` | Open the floating chat widget |
|
|
370
|
+
| `closeChat()` | `void` | Close the floating chat widget |
|
|
371
|
+
| `clearConversation()` | `void` | Clear all messages and reset conversation |
|
|
372
|
+
| `getMessages()` | `Message[]` | Get current messages array |
|
|
373
|
+
| `getToken()` | `string \| null` | Get current conversation token |
|
|
374
|
+
|
|
375
|
+
### Complete Example with External Controls
|
|
376
|
+
|
|
377
|
+
```tsx
|
|
378
|
+
import React, { useRef, useState } from 'react';
|
|
379
|
+
import { BlumessageChat, BlumessageChatRef } from '@blumessage/react-chat';
|
|
380
|
+
|
|
381
|
+
function App() {
|
|
382
|
+
const [externalMessage, setExternalMessage] = useState('');
|
|
383
|
+
const chatRef = useRef<BlumessageChatRef>(null);
|
|
384
|
+
|
|
385
|
+
const handleSendExternalMessage = async () => {
|
|
386
|
+
if (!externalMessage.trim() || !chatRef.current) return;
|
|
387
|
+
|
|
388
|
+
try {
|
|
389
|
+
await chatRef.current.sendMessage(externalMessage);
|
|
390
|
+
setExternalMessage('');
|
|
391
|
+
console.log('External message sent successfully');
|
|
392
|
+
} catch (error) {
|
|
393
|
+
console.error('Failed to send external message:', error);
|
|
394
|
+
}
|
|
395
|
+
};
|
|
396
|
+
|
|
397
|
+
return (
|
|
398
|
+
<div>
|
|
399
|
+
<div style={{ marginBottom: '20px' }}>
|
|
400
|
+
<input
|
|
401
|
+
type="text"
|
|
402
|
+
value={externalMessage}
|
|
403
|
+
onChange={(e) => setExternalMessage(e.target.value)}
|
|
404
|
+
placeholder="Type a message to send externally..."
|
|
405
|
+
onKeyPress={(e) => e.key === 'Enter' && handleSendExternalMessage()}
|
|
406
|
+
/>
|
|
407
|
+
<button onClick={handleSendExternalMessage}>Send</button>
|
|
408
|
+
<button onClick={() => chatRef.current?.openChat()}>Open Chat</button>
|
|
409
|
+
<button onClick={() => chatRef.current?.closeChat()}>Close Chat</button>
|
|
410
|
+
<button onClick={() => chatRef.current?.clearConversation()}>Clear Chat</button>
|
|
411
|
+
</div>
|
|
412
|
+
|
|
413
|
+
<BlumessageChat
|
|
414
|
+
ref={chatRef}
|
|
415
|
+
apiKey="your-api-key"
|
|
416
|
+
floating={true}
|
|
417
|
+
onUserMessage={(message) => console.log('User message:', message)}
|
|
418
|
+
onAssistantMessage={(message) => console.log('Assistant message:', message)}
|
|
419
|
+
/>
|
|
420
|
+
</div>
|
|
421
|
+
);
|
|
422
|
+
}
|
|
423
|
+
```
|
|
424
|
+
|
|
276
425
|
## TypeScript Support
|
|
277
426
|
|
|
278
427
|
Full TypeScript definitions included:
|
|
279
428
|
|
|
280
429
|
```typescript
|
|
281
|
-
import { BlumessageChat, Message, BlumessageChatProps } from '@blumessage/react-chat';
|
|
430
|
+
import { BlumessageChat, Message, BlumessageChatProps, BlumessageChatRef } from '@blumessage/react-chat';
|
|
282
431
|
|
|
283
432
|
interface Message {
|
|
284
433
|
id: string;
|
|
@@ -286,6 +435,15 @@ interface Message {
|
|
|
286
435
|
content: string;
|
|
287
436
|
timestamp: number;
|
|
288
437
|
}
|
|
438
|
+
|
|
439
|
+
interface BlumessageChatRef {
|
|
440
|
+
sendMessage: (message: string) => Promise<void>;
|
|
441
|
+
openChat: () => void;
|
|
442
|
+
closeChat: () => void;
|
|
443
|
+
clearConversation: () => void;
|
|
444
|
+
getMessages: () => Message[];
|
|
445
|
+
getToken: () => string | null;
|
|
446
|
+
}
|
|
289
447
|
```
|
|
290
448
|
|
|
291
449
|
## Storage Behavior
|
|
@@ -301,6 +459,32 @@ interface Message {
|
|
|
301
459
|
- React 18+
|
|
302
460
|
- TypeScript 4.5+
|
|
303
461
|
|
|
462
|
+
## Building
|
|
463
|
+
|
|
464
|
+
### Standard Build (ES Modules)
|
|
465
|
+
|
|
466
|
+
```bash
|
|
467
|
+
npm run build
|
|
468
|
+
```
|
|
469
|
+
|
|
470
|
+
### Browser Versions
|
|
471
|
+
|
|
472
|
+
#### Standalone (Includes React)
|
|
473
|
+
|
|
474
|
+
```bash
|
|
475
|
+
npm run build:commonjs
|
|
476
|
+
```
|
|
477
|
+
|
|
478
|
+
This creates `dist/index.js` which includes React and can be used directly in browsers.
|
|
479
|
+
|
|
480
|
+
#### External React (Smaller Bundle)
|
|
481
|
+
|
|
482
|
+
```bash
|
|
483
|
+
npm run build:browser
|
|
484
|
+
```
|
|
485
|
+
|
|
486
|
+
This creates `dist/blumessage-chat.browser.js` which requires React to be loaded separately.
|
|
487
|
+
|
|
304
488
|
## License
|
|
305
489
|
|
|
306
490
|
UNLICENSED - For use only by customers with an active Blumessage subscription.
|
package/dist/BlumessageChat.js
CHANGED
|
@@ -55,7 +55,7 @@ var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) {
|
|
|
55
55
|
return to.concat(ar || Array.prototype.slice.call(from));
|
|
56
56
|
};
|
|
57
57
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
58
|
-
import React, { useState, useEffect, useRef } from "react";
|
|
58
|
+
import React, { useState, useEffect, useRef, forwardRef, useImperativeHandle } from "react";
|
|
59
59
|
import ReactMarkdown from 'react-markdown';
|
|
60
60
|
import remarkGfm from 'remark-gfm';
|
|
61
61
|
import { MessageCircle, AlertTriangle, Loader2, Send, X, Maximize, Minimize2, Bot, MessageSquare, Phone, Mail, Headphones, Users, User, Heart, Star, Zap } from "lucide-react";
|
|
@@ -119,7 +119,7 @@ var useIsMobile = function () {
|
|
|
119
119
|
}, []);
|
|
120
120
|
return isMobile;
|
|
121
121
|
};
|
|
122
|
-
export var BlumessageChat = function (_a) {
|
|
122
|
+
export var BlumessageChat = forwardRef(function (_a, ref) {
|
|
123
123
|
var apiKey = _a.apiKey, _b = _a.placeholder, placeholder = _b === void 0 ? "Type your message..." : _b, _c = _a.theme, theme = _c === void 0 ? 'light' : _c, width = _a.width, height = _a.height, _d = _a.size, size = _d === void 0 ? 'medium' : _d, _e = _a.name, name = _e === void 0 ? "Blumessage AI" : _e, _f = _a.subtitle, subtitle = _f === void 0 ? "Online • Instant responses" : _f, _g = _a.initialMessages, initialMessages = _g === void 0 ? [] : _g, onUserMessage = _a.onUserMessage, onAssistantMessage = _a.onAssistantMessage, initialToken = _a.token, onTokenChange = _a.onTokenChange, onChatWidgetOpen = _a.onChatWidgetOpen, onChatWidgetClosed = _a.onChatWidgetClosed, onError = _a.onError, _h = _a.persistent, persistent = _h === void 0 ? false : _h, _j = _a.showTimestamps, showTimestamps = _j === void 0 ? false : _j, _k = _a.typingText, typingText = _k === void 0 ? "Agent is typing..." : _k, _l = _a.emptyStateText, emptyStateText = _l === void 0 ? "Start a conversation!" : _l, _m = _a.markdown, markdown = _m === void 0 ? true : _m,
|
|
124
124
|
// Floating button props
|
|
125
125
|
_o = _a.floating,
|
|
@@ -203,7 +203,7 @@ export var BlumessageChat = function (_a) {
|
|
|
203
203
|
break;
|
|
204
204
|
case 'medium':
|
|
205
205
|
default:
|
|
206
|
-
dimensions = { width: '
|
|
206
|
+
dimensions = { width: '600px', height: '600px' };
|
|
207
207
|
}
|
|
208
208
|
return dimensions;
|
|
209
209
|
};
|
|
@@ -563,6 +563,149 @@ export var BlumessageChat = function (_a) {
|
|
|
563
563
|
// Default fallback
|
|
564
564
|
return MessageCircle;
|
|
565
565
|
};
|
|
566
|
+
// Expose methods to parent component via ref
|
|
567
|
+
useImperativeHandle(ref, function () { return ({
|
|
568
|
+
sendMessage: function (message) { return __awaiter(void 0, void 0, void 0, function () {
|
|
569
|
+
var userMessage, currentInput, requestBody, response, apiResponse, assistantMessages, latestAssistantMessage, assistantResponse_2, errorMessage_3, error_3, errorMessage_4;
|
|
570
|
+
return __generator(this, function (_a) {
|
|
571
|
+
switch (_a.label) {
|
|
572
|
+
case 0:
|
|
573
|
+
if (!message.trim() || isLoading)
|
|
574
|
+
return [2 /*return*/];
|
|
575
|
+
userMessage = {
|
|
576
|
+
id: Date.now().toString(),
|
|
577
|
+
content: message.trim(),
|
|
578
|
+
role: 'user',
|
|
579
|
+
timestamp: Date.now()
|
|
580
|
+
};
|
|
581
|
+
// Add user message to UI immediately
|
|
582
|
+
setMessages(function (prev) { return __spreadArray(__spreadArray([], prev, true), [userMessage], false); });
|
|
583
|
+
currentInput = message.trim();
|
|
584
|
+
setIsLoading(true);
|
|
585
|
+
// Call the callback if provided
|
|
586
|
+
if (onUserMessage) {
|
|
587
|
+
onUserMessage(userMessage);
|
|
588
|
+
}
|
|
589
|
+
_a.label = 1;
|
|
590
|
+
case 1:
|
|
591
|
+
_a.trys.push([1, 6, 7, 8]);
|
|
592
|
+
requestBody = {
|
|
593
|
+
message: currentInput,
|
|
594
|
+
};
|
|
595
|
+
// Include token if we have one
|
|
596
|
+
if (token) {
|
|
597
|
+
requestBody.token = token;
|
|
598
|
+
}
|
|
599
|
+
return [4 /*yield*/, fetch('https://api.blumessage.com/api/v1/conversations', {
|
|
600
|
+
method: 'POST',
|
|
601
|
+
headers: {
|
|
602
|
+
'Content-Type': 'application/json',
|
|
603
|
+
'Authorization': "Bearer ".concat(apiKey),
|
|
604
|
+
},
|
|
605
|
+
body: JSON.stringify(requestBody),
|
|
606
|
+
})];
|
|
607
|
+
case 2:
|
|
608
|
+
response = _a.sent();
|
|
609
|
+
if (!response.ok) return [3 /*break*/, 4];
|
|
610
|
+
return [4 /*yield*/, response.json()];
|
|
611
|
+
case 3:
|
|
612
|
+
apiResponse = _a.sent();
|
|
613
|
+
// Update token from response (for both first message and continuing conversations)
|
|
614
|
+
updateConversationToken(apiResponse.token);
|
|
615
|
+
assistantMessages = apiResponse.messages.filter(function (msg) { return msg.role === 'assistant'; });
|
|
616
|
+
latestAssistantMessage = assistantMessages[assistantMessages.length - 1];
|
|
617
|
+
if (latestAssistantMessage) {
|
|
618
|
+
assistantResponse_2 = {
|
|
619
|
+
id: (Date.now() + 1).toString(),
|
|
620
|
+
content: latestAssistantMessage.content,
|
|
621
|
+
role: 'assistant',
|
|
622
|
+
timestamp: latestAssistantMessage.timestamp,
|
|
623
|
+
};
|
|
624
|
+
setMessages(function (prev) { return __spreadArray(__spreadArray([], prev, true), [assistantResponse_2], false); });
|
|
625
|
+
if (onAssistantMessage) {
|
|
626
|
+
onAssistantMessage(assistantResponse_2);
|
|
627
|
+
}
|
|
628
|
+
}
|
|
629
|
+
return [3 /*break*/, 5];
|
|
630
|
+
case 4:
|
|
631
|
+
console.error('Failed to send message to Blumessage API:', response.status, response.statusText);
|
|
632
|
+
// If token is invalid, clear it
|
|
633
|
+
if (response.status === 401 || response.status === 403) {
|
|
634
|
+
updateConversationToken(null);
|
|
635
|
+
}
|
|
636
|
+
errorMessage_3 = {
|
|
637
|
+
id: (Date.now() + 1).toString(),
|
|
638
|
+
content: "Sorry, I'm having trouble connecting right now. Please try again.",
|
|
639
|
+
role: 'assistant',
|
|
640
|
+
timestamp: Date.now(),
|
|
641
|
+
};
|
|
642
|
+
setMessages(function (prev) { return __spreadArray(__spreadArray([], prev, true), [errorMessage_3], false); });
|
|
643
|
+
if (onError) {
|
|
644
|
+
onError("Failed to send message: ".concat(response.status, " ").concat(response.statusText), "message_send");
|
|
645
|
+
}
|
|
646
|
+
_a.label = 5;
|
|
647
|
+
case 5: return [3 /*break*/, 8];
|
|
648
|
+
case 6:
|
|
649
|
+
error_3 = _a.sent();
|
|
650
|
+
console.error('Error sending message to Blumessage API:', error_3);
|
|
651
|
+
errorMessage_4 = {
|
|
652
|
+
id: (Date.now() + 1).toString(),
|
|
653
|
+
content: "Sorry, I'm having trouble connecting right now. Please try again.",
|
|
654
|
+
role: 'assistant',
|
|
655
|
+
timestamp: Date.now(),
|
|
656
|
+
};
|
|
657
|
+
setMessages(function (prev) { return __spreadArray(__spreadArray([], prev, true), [errorMessage_4], false); });
|
|
658
|
+
if (onError) {
|
|
659
|
+
onError("Error sending message: ".concat(error_3), "message_send");
|
|
660
|
+
}
|
|
661
|
+
return [3 /*break*/, 8];
|
|
662
|
+
case 7:
|
|
663
|
+
setIsLoading(false);
|
|
664
|
+
return [7 /*endfinally*/];
|
|
665
|
+
case 8: return [2 /*return*/];
|
|
666
|
+
}
|
|
667
|
+
});
|
|
668
|
+
}); },
|
|
669
|
+
openChat: function () {
|
|
670
|
+
if (!isOpen && !isAnimating) {
|
|
671
|
+
setIsAnimating(true);
|
|
672
|
+
// Small delay to ensure smooth animation start
|
|
673
|
+
setTimeout(function () {
|
|
674
|
+
setIsOpen(true);
|
|
675
|
+
// Call the callback after the chat is open
|
|
676
|
+
if (onChatWidgetOpen) {
|
|
677
|
+
onChatWidgetOpen();
|
|
678
|
+
}
|
|
679
|
+
}, 10);
|
|
680
|
+
// Animation duration
|
|
681
|
+
setTimeout(function () {
|
|
682
|
+
setIsAnimating(false);
|
|
683
|
+
}, 300);
|
|
684
|
+
}
|
|
685
|
+
},
|
|
686
|
+
closeChat: function () {
|
|
687
|
+
if (isOpen && !isAnimating) {
|
|
688
|
+
setIsAnimating(true);
|
|
689
|
+
setIsOpen(false);
|
|
690
|
+
// Reset maximized state when closing
|
|
691
|
+
setIsMaximized(false);
|
|
692
|
+
// Call the callback when closing
|
|
693
|
+
if (onChatWidgetClosed) {
|
|
694
|
+
onChatWidgetClosed();
|
|
695
|
+
}
|
|
696
|
+
// Wait for animation to complete
|
|
697
|
+
setTimeout(function () {
|
|
698
|
+
setIsAnimating(false);
|
|
699
|
+
}, 300);
|
|
700
|
+
}
|
|
701
|
+
},
|
|
702
|
+
clearConversation: function () {
|
|
703
|
+
setMessages([]);
|
|
704
|
+
updateConversationToken(null);
|
|
705
|
+
},
|
|
706
|
+
getMessages: function () { return messages; },
|
|
707
|
+
getToken: function () { return token; },
|
|
708
|
+
}); });
|
|
566
709
|
// Helper function to get position styles for floating button
|
|
567
710
|
var getButtonPositionStyles = function () {
|
|
568
711
|
var baseStyles = {
|
|
@@ -783,4 +926,4 @@ export var BlumessageChat = function (_a) {
|
|
|
783
926
|
return (_jsxs(_Fragment, { children: [_jsxs("button", { onClick: handleOpenChat, className: "text-white rounded-full shadow-lg transition-all duration-200 flex items-center justify-center gap-2 ".concat(isMobile ? 'blumessage-mobile-button' : '', " ").concat(buttonText ? 'px-4 py-3 h-12' : 'w-14 h-14'), style: __assign(__assign(__assign({}, getButtonPositionStyles()), buttonStyle), { backgroundImage: primaryColor }), children: [React.createElement(getIconComponent(), { className: "w-6 h-6" }), buttonText && !isMobile && _jsx("span", { className: "text-sm font-medium whitespace-nowrap", children: buttonText })] }), renderChatWindow()] }));
|
|
784
927
|
}
|
|
785
928
|
return renderChatWindow();
|
|
786
|
-
};
|
|
929
|
+
});
|