@blumessage/react-chat 1.2.1 → 1.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +131 -1
- package/dist/BlumessageChat.js +147 -4
- package/dist/types/BlumessageChat.d.ts +9 -1
- package/dist/types/index.d.ts +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -273,12 +273,133 @@ The `onError` callback provides detailed error context:
|
|
|
273
273
|
/>
|
|
274
274
|
```
|
|
275
275
|
|
|
276
|
+
## External Message Submission
|
|
277
|
+
|
|
278
|
+
The component supports external message submission through a ref, allowing parent components to programmatically send messages and control the chat widget.
|
|
279
|
+
|
|
280
|
+
### Using the Ref
|
|
281
|
+
|
|
282
|
+
```tsx
|
|
283
|
+
import React, { useRef } from 'react';
|
|
284
|
+
import { BlumessageChat, BlumessageChatRef } from '@blumessage/react-chat';
|
|
285
|
+
|
|
286
|
+
function App() {
|
|
287
|
+
const chatRef = useRef<BlumessageChatRef>(null);
|
|
288
|
+
|
|
289
|
+
const sendExternalMessage = async () => {
|
|
290
|
+
if (chatRef.current) {
|
|
291
|
+
await chatRef.current.sendMessage("Hello from external component!");
|
|
292
|
+
}
|
|
293
|
+
};
|
|
294
|
+
|
|
295
|
+
const openChat = () => {
|
|
296
|
+
chatRef.current?.openChat();
|
|
297
|
+
};
|
|
298
|
+
|
|
299
|
+
const closeChat = () => {
|
|
300
|
+
chatRef.current?.closeChat();
|
|
301
|
+
};
|
|
302
|
+
|
|
303
|
+
const clearChat = () => {
|
|
304
|
+
chatRef.current?.clearConversation();
|
|
305
|
+
};
|
|
306
|
+
|
|
307
|
+
const getMessages = () => {
|
|
308
|
+
const messages = chatRef.current?.getMessages();
|
|
309
|
+
console.log('Current messages:', messages);
|
|
310
|
+
};
|
|
311
|
+
|
|
312
|
+
const getToken = () => {
|
|
313
|
+
const token = chatRef.current?.getToken();
|
|
314
|
+
console.log('Current conversation token:', token);
|
|
315
|
+
};
|
|
316
|
+
|
|
317
|
+
return (
|
|
318
|
+
<div>
|
|
319
|
+
<button onClick={sendExternalMessage}>Send External Message</button>
|
|
320
|
+
<button onClick={openChat}>Open Chat</button>
|
|
321
|
+
<button onClick={closeChat}>Close Chat</button>
|
|
322
|
+
<button onClick={clearChat}>Clear Chat</button>
|
|
323
|
+
<button onClick={getMessages}>Get Messages</button>
|
|
324
|
+
<button onClick={getToken}>Get Token</button>
|
|
325
|
+
|
|
326
|
+
<BlumessageChat
|
|
327
|
+
ref={chatRef}
|
|
328
|
+
apiKey="your-api-key"
|
|
329
|
+
floating={true}
|
|
330
|
+
/>
|
|
331
|
+
</div>
|
|
332
|
+
);
|
|
333
|
+
}
|
|
334
|
+
```
|
|
335
|
+
|
|
336
|
+
### Available Ref Methods
|
|
337
|
+
|
|
338
|
+
| Method | Type | Description |
|
|
339
|
+
|--------|------|-------------|
|
|
340
|
+
| `sendMessage(message: string)` | `Promise<void>` | Send a message programmatically |
|
|
341
|
+
| `openChat()` | `void` | Open the floating chat widget |
|
|
342
|
+
| `closeChat()` | `void` | Close the floating chat widget |
|
|
343
|
+
| `clearConversation()` | `void` | Clear all messages and reset conversation |
|
|
344
|
+
| `getMessages()` | `Message[]` | Get current messages array |
|
|
345
|
+
| `getToken()` | `string \| null` | Get current conversation token |
|
|
346
|
+
|
|
347
|
+
### Complete Example with External Controls
|
|
348
|
+
|
|
349
|
+
```tsx
|
|
350
|
+
import React, { useRef, useState } from 'react';
|
|
351
|
+
import { BlumessageChat, BlumessageChatRef } from '@blumessage/react-chat';
|
|
352
|
+
|
|
353
|
+
function App() {
|
|
354
|
+
const [externalMessage, setExternalMessage] = useState('');
|
|
355
|
+
const chatRef = useRef<BlumessageChatRef>(null);
|
|
356
|
+
|
|
357
|
+
const handleSendExternalMessage = async () => {
|
|
358
|
+
if (!externalMessage.trim() || !chatRef.current) return;
|
|
359
|
+
|
|
360
|
+
try {
|
|
361
|
+
await chatRef.current.sendMessage(externalMessage);
|
|
362
|
+
setExternalMessage('');
|
|
363
|
+
console.log('External message sent successfully');
|
|
364
|
+
} catch (error) {
|
|
365
|
+
console.error('Failed to send external message:', error);
|
|
366
|
+
}
|
|
367
|
+
};
|
|
368
|
+
|
|
369
|
+
return (
|
|
370
|
+
<div>
|
|
371
|
+
<div style={{ marginBottom: '20px' }}>
|
|
372
|
+
<input
|
|
373
|
+
type="text"
|
|
374
|
+
value={externalMessage}
|
|
375
|
+
onChange={(e) => setExternalMessage(e.target.value)}
|
|
376
|
+
placeholder="Type a message to send externally..."
|
|
377
|
+
onKeyPress={(e) => e.key === 'Enter' && handleSendExternalMessage()}
|
|
378
|
+
/>
|
|
379
|
+
<button onClick={handleSendExternalMessage}>Send</button>
|
|
380
|
+
<button onClick={() => chatRef.current?.openChat()}>Open Chat</button>
|
|
381
|
+
<button onClick={() => chatRef.current?.closeChat()}>Close Chat</button>
|
|
382
|
+
<button onClick={() => chatRef.current?.clearConversation()}>Clear Chat</button>
|
|
383
|
+
</div>
|
|
384
|
+
|
|
385
|
+
<BlumessageChat
|
|
386
|
+
ref={chatRef}
|
|
387
|
+
apiKey="your-api-key"
|
|
388
|
+
floating={true}
|
|
389
|
+
onUserMessage={(message) => console.log('User message:', message)}
|
|
390
|
+
onAssistantMessage={(message) => console.log('Assistant message:', message)}
|
|
391
|
+
/>
|
|
392
|
+
</div>
|
|
393
|
+
);
|
|
394
|
+
}
|
|
395
|
+
```
|
|
396
|
+
|
|
276
397
|
## TypeScript Support
|
|
277
398
|
|
|
278
399
|
Full TypeScript definitions included:
|
|
279
400
|
|
|
280
401
|
```typescript
|
|
281
|
-
import { BlumessageChat, Message, BlumessageChatProps } from '@blumessage/react-chat';
|
|
402
|
+
import { BlumessageChat, Message, BlumessageChatProps, BlumessageChatRef } from '@blumessage/react-chat';
|
|
282
403
|
|
|
283
404
|
interface Message {
|
|
284
405
|
id: string;
|
|
@@ -286,6 +407,15 @@ interface Message {
|
|
|
286
407
|
content: string;
|
|
287
408
|
timestamp: number;
|
|
288
409
|
}
|
|
410
|
+
|
|
411
|
+
interface BlumessageChatRef {
|
|
412
|
+
sendMessage: (message: string) => Promise<void>;
|
|
413
|
+
openChat: () => void;
|
|
414
|
+
closeChat: () => void;
|
|
415
|
+
clearConversation: () => void;
|
|
416
|
+
getMessages: () => Message[];
|
|
417
|
+
getToken: () => string | null;
|
|
418
|
+
}
|
|
289
419
|
```
|
|
290
420
|
|
|
291
421
|
## Storage Behavior
|
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
|
+
});
|
|
@@ -11,6 +11,14 @@ export interface BlumessageApiResponse {
|
|
|
11
11
|
created_at: number;
|
|
12
12
|
updated_at: number;
|
|
13
13
|
}
|
|
14
|
+
export interface BlumessageChatRef {
|
|
15
|
+
sendMessage: (message: string) => Promise<void>;
|
|
16
|
+
openChat: () => void;
|
|
17
|
+
closeChat: () => void;
|
|
18
|
+
clearConversation: () => void;
|
|
19
|
+
getMessages: () => Message[];
|
|
20
|
+
getToken: () => string | null;
|
|
21
|
+
}
|
|
14
22
|
export interface BlumessageChatProps {
|
|
15
23
|
apiKey: string;
|
|
16
24
|
placeholder?: string;
|
|
@@ -43,4 +51,4 @@ export interface BlumessageChatProps {
|
|
|
43
51
|
icon?: string;
|
|
44
52
|
primaryColor?: string;
|
|
45
53
|
}
|
|
46
|
-
export declare const BlumessageChat: React.
|
|
54
|
+
export declare const BlumessageChat: React.ForwardRefExoticComponent<BlumessageChatProps & React.RefAttributes<BlumessageChatRef>>;
|
package/dist/types/index.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export { BlumessageChat, type BlumessageChatProps, type Message, type BlumessageApiResponse } from './BlumessageChat';
|
|
1
|
+
export { BlumessageChat, type BlumessageChatProps, type BlumessageChatRef, type Message, type BlumessageApiResponse } from './BlumessageChat';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@blumessage/react-chat",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.3.0",
|
|
4
4
|
"description": "A React TypeScript chat widget component with floating button, theming, and Blumessage API integration",
|
|
5
5
|
"license": "UNLICENSED",
|
|
6
6
|
"author": "Blumessage <contact@blumessage.com>",
|