@blumessage/react-chat 1.0.6 → 1.0.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -83,6 +83,7 @@ function App() {
83
83
  | `initialMessages` | `Message[]` | `[]` | Pre-populate with messages |
84
84
  | `conversationId` | `string` | - | Continue specific conversation |
85
85
  | `persistent` | `boolean` | `false` | Use localStorage vs sessionStorage |
86
+ | `showTimestamps` | `boolean` | `false` | Display timestamps: today="14:00", older="17 July, 13:00" |
86
87
  | **Event Callbacks** |
87
88
  | `onUserMessage` | `(message: Message) => void` | - | Called when user sends message |
88
89
  | `onAssistantMessage` | `(message: Message) => void` | - | Called when assistant responds |
@@ -155,6 +156,30 @@ const initialMessages = [
155
156
  />
156
157
  ```
157
158
 
159
+ ### Chat with Timestamps
160
+
161
+ ```tsx
162
+ <BlumessageChat
163
+ apiKey="your-api-key"
164
+ showTimestamps={true} // Shows "14:00" or "17 July, 13:00"
165
+ floating={false}
166
+ initialMessages={[
167
+ {
168
+ id: '1',
169
+ role: 'assistant' as const,
170
+ content: 'Message from today',
171
+ timestamp: Date.now() - (60 * 60 * 1000) // Shows: "14:00" (or "2:00 PM")
172
+ },
173
+ {
174
+ id: '2',
175
+ role: 'user' as const,
176
+ content: 'Message from yesterday',
177
+ timestamp: Date.now() - (24 * 60 * 60 * 1000) // Shows: "17 July, 13:00"
178
+ }
179
+ ]}
180
+ />
181
+ ```
182
+
158
183
  ## Icon Options
159
184
 
160
185
  The `icon` prop accepts **any lucide-react icon name** with flexible naming patterns. The component intelligently matches your input to the appropriate lucide-react icon:
@@ -101,26 +101,54 @@ var clearStoredConversationId = function (persistent) {
101
101
  }
102
102
  };
103
103
  export var BlumessageChat = function (_a) {
104
- 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, initialConversationId = _a.conversationId, onConversationIdChange = _a.onConversationIdChange, onChatWidgetOpen = _a.onChatWidgetOpen, onChatWidgetClosed = _a.onChatWidgetClosed, onError = _a.onError, _h = _a.persistent, persistent = _h === void 0 ? false : _h,
104
+ 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, initialConversationId = _a.conversationId, onConversationIdChange = _a.onConversationIdChange, 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,
105
105
  // Floating button props
106
- _j = _a.floating,
106
+ _k = _a.floating,
107
107
  // Floating button props
108
- floating = _j === void 0 ? true : _j, _k = _a.buttonText, buttonText = _k === void 0 ? "Chat with us" : _k, _l = _a.buttonPosition, buttonPosition = _l === void 0 ? 'bottom-right' : _l, buttonStyle = _a.buttonStyle, _m = _a.defaultOpen, defaultOpen = _m === void 0 ? false : _m, _o = _a.maximizeToggleButton, maximizeToggleButton = _o === void 0 ? true : _o, _p = _a.fullScreen, fullScreen = _p === void 0 ? false : _p, _q = _a.icon, icon = _q === void 0 ? 'message-circle' : _q,
108
+ floating = _k === void 0 ? true : _k, _l = _a.buttonText, buttonText = _l === void 0 ? "Chat with us" : _l, _m = _a.buttonPosition, buttonPosition = _m === void 0 ? 'bottom-right' : _m, buttonStyle = _a.buttonStyle, _o = _a.defaultOpen, defaultOpen = _o === void 0 ? false : _o, _p = _a.maximizeToggleButton, maximizeToggleButton = _p === void 0 ? true : _p, _q = _a.fullScreen, fullScreen = _q === void 0 ? false : _q, _r = _a.icon, icon = _r === void 0 ? 'message-circle' : _r,
109
109
  // Styling props
110
- _r = _a.primaryColor,
110
+ _s = _a.primaryColor,
111
111
  // Styling props
112
- primaryColor = _r === void 0 ? "linear-gradient(to right, #3b82f6,rgb(8, 98, 242))" : _r;
113
- var _s = useState(false), isInitialized = _s[0], setIsInitialized = _s[1];
114
- var _t = useState(null), error = _t[0], setError = _t[1];
115
- var _u = useState(initialMessages), messages = _u[0], setMessages = _u[1];
116
- var _v = useState(''), inputValue = _v[0], setInputValue = _v[1];
117
- var _w = useState(initialConversationId || getStoredConversationId(persistent)), conversationId = _w[0], setConversationId = _w[1];
118
- var _x = useState(defaultOpen), isOpen = _x[0], setIsOpen = _x[1];
119
- var _y = useState(false), isAnimating = _y[0], setIsAnimating = _y[1];
120
- var _z = useState(false), isLoading = _z[0], setIsLoading = _z[1];
121
- var _0 = useState(false), isMaximized = _0[0], setIsMaximized = _0[1];
112
+ primaryColor = _s === void 0 ? "linear-gradient(to right, #3b82f6,rgb(8, 98, 242))" : _s;
113
+ var _t = useState(false), isInitialized = _t[0], setIsInitialized = _t[1];
114
+ var _u = useState(null), error = _u[0], setError = _u[1];
115
+ var _v = useState(initialMessages), messages = _v[0], setMessages = _v[1];
116
+ var _w = useState(''), inputValue = _w[0], setInputValue = _w[1];
117
+ var _x = useState(initialConversationId || getStoredConversationId(persistent)), conversationId = _x[0], setConversationId = _x[1];
118
+ var _y = useState(defaultOpen), isOpen = _y[0], setIsOpen = _y[1];
119
+ var _z = useState(false), isAnimating = _z[0], setIsAnimating = _z[1];
120
+ var _0 = useState(false), isLoading = _0[0], setIsLoading = _0[1];
121
+ var _1 = useState(false), isMaximized = _1[0], setIsMaximized = _1[1];
122
122
  var messagesEndRef = useRef(null);
123
123
  var isInitialLoad = useRef(true);
124
+ // Helper function to format timestamp
125
+ var formatTimestamp = function (timestamp) {
126
+ var date = new Date(timestamp);
127
+ var now = new Date();
128
+ // Check if the message is from today
129
+ var isToday = date.toDateString() === now.toDateString();
130
+ if (isToday) {
131
+ // If today: show time only in user's locale format (14:00 or 2:00 PM)
132
+ return date.toLocaleTimeString(undefined, {
133
+ hour: '2-digit',
134
+ minute: '2-digit',
135
+ hour12: undefined // Let browser decide based on user's locale
136
+ });
137
+ }
138
+ else {
139
+ // If more than a day: show "17 July, 13:00" format in user's locale
140
+ var dateStr = date.toLocaleDateString(undefined, {
141
+ day: 'numeric',
142
+ month: 'long'
143
+ });
144
+ var timeStr = date.toLocaleTimeString(undefined, {
145
+ hour: '2-digit',
146
+ minute: '2-digit',
147
+ hour12: undefined // Let browser decide based on user's locale
148
+ });
149
+ return "".concat(dateStr, ", ").concat(timeStr);
150
+ }
151
+ };
124
152
  // Helper function to get dimensions based on size
125
153
  var getDimensions = function () {
126
154
  // If custom width/height are provided, use them
@@ -142,7 +170,7 @@ export var BlumessageChat = function (_a) {
142
170
  }
143
171
  return dimensions;
144
172
  };
145
- var _1 = getDimensions(), actualWidth = _1.width, actualHeight = _1.height;
173
+ var _2 = getDimensions(), actualWidth = _2.width, actualHeight = _2.height;
146
174
  // Function to update conversation ID and notify parent
147
175
  var updateConversationId = function (newConversationId) {
148
176
  setConversationId(newConversationId);
@@ -580,17 +608,30 @@ export var BlumessageChat = function (_a) {
580
608
  height: (fullScreen || isMaximized) ? '100%' : actualHeight,
581
609
  fontFamily: 'system-ui, -apple-system, sans-serif',
582
610
  borderRadius: (fullScreen || isMaximized) ? '12px' : '24px'
583
- }, children: [_jsxs("div", { className: "flex items-center px-6 py-4 border-b ".concat(theme === 'dark'
611
+ }, children: [_jsxs("div", { className: "border-b ".concat(theme === 'dark'
584
612
  ? 'bg-gray-900 border-gray-700'
585
- : 'bg-white border-gray-100'), children: [_jsx("div", { className: "w-12 h-12 rounded-full flex items-center justify-center mr-4", style: { backgroundImage: primaryColor }, children: React.createElement(getIconComponent(), { className: "w-6 h-6 text-white" }) }), _jsxs("div", { className: "flex-1", children: [_jsx("div", { className: "text-lg font-semibold m-0 leading-6 ".concat(theme === 'dark' ? 'text-gray-100' : 'text-gray-900'), children: name }), _jsx("div", { className: "text-sm m-0 leading-5 ".concat(theme === 'dark' ? 'text-gray-400' : 'text-gray-500'), children: subtitle })] }), floating && (_jsxs("div", { className: "flex items-center gap-1", children: [maximizeToggleButton && (_jsx("button", { onClick: handleToggleMaximize, className: "w-8 h-8 rounded-full flex items-center justify-center cursor-pointer transition-colors ".concat(theme === 'dark'
613
+ : 'bg-white border-gray-100'), style: {
614
+ display: 'flex',
615
+ alignItems: 'center',
616
+ padding: '16px 24px'
617
+ }, children: [_jsx("div", { className: "rounded-full flex items-center justify-center", style: {
618
+ backgroundImage: primaryColor,
619
+ width: '48px',
620
+ height: '48px',
621
+ marginRight: '24px'
622
+ }, children: React.createElement(getIconComponent(), { className: "w-6 h-6 text-white" }) }), _jsxs("div", { style: { flex: 1 }, children: [_jsx("div", { className: "text-lg font-semibold m-0 leading-6 ".concat(theme === 'dark' ? 'text-gray-100' : 'text-gray-900'), children: name }), _jsx("div", { className: "text-sm m-0 leading-5 ".concat(theme === 'dark' ? 'text-gray-400' : 'text-gray-500'), children: subtitle })] }), floating && (_jsxs("div", { className: "flex items-center gap-1", children: [maximizeToggleButton && (_jsx("button", { onClick: handleToggleMaximize, className: "w-8 h-8 rounded-full flex items-center justify-center cursor-pointer transition-colors ".concat(theme === 'dark'
586
623
  ? 'hover:bg-gray-800 text-gray-400 hover:text-gray-200'
587
624
  : 'hover:bg-gray-100 text-gray-500 hover:text-gray-700'), children: isMaximized ? _jsx(Minimize2, { className: "w-4 h-4" }) : _jsx(Maximize, { className: "w-4 h-4" }) })), _jsx("button", { onClick: handleCloseChat, className: "w-8 h-8 rounded-full flex items-center justify-center cursor-pointer transition-colors ".concat(theme === 'dark'
588
625
  ? 'hover:bg-gray-800 text-gray-400 hover:text-gray-200'
589
- : 'hover:bg-gray-100 text-gray-500 hover:text-gray-700'), children: _jsx(X, { className: "w-4 h-4" }) })] }))] }), _jsxs("div", { className: "flex-1 px-6 py-4 flex flex-col gap-4 overflow-y-auto ".concat(theme === 'dark' ? 'bg-gray-800' : 'bg-gray-50'), children: [messages.map(function (message) { return (_jsx("div", { className: "flex ".concat(message.role === 'user' ? 'justify-end' : 'justify-start'), children: _jsx("div", { className: "px-4 py-3 rounded-2xl max-w-[80%] text-sm leading-6 ".concat(message.role === 'user'
590
- ? 'text-white'
591
- : theme === 'dark'
592
- ? 'bg-gray-700 text-gray-100 border border-gray-600 shadow-sm'
593
- : 'bg-white text-gray-800 border border-gray-200 shadow-sm'), style: message.role === 'user' ? { backgroundImage: primaryColor } : {}, children: message.content }) }, message.id)); }), isLoading && (_jsx("div", { className: "flex justify-start", children: _jsx("div", { className: "px-4 py-3 rounded-2xl max-w-[80%] text-sm leading-6 shadow-sm ".concat(theme === 'dark'
626
+ : 'hover:bg-gray-100 text-gray-500 hover:text-gray-700'), children: _jsx(X, { className: "w-4 h-4" }) })] }))] }), _jsxs("div", { className: "flex-1 px-6 py-4 flex flex-col gap-4 overflow-y-auto ".concat(theme === 'dark' ? 'bg-gray-800' : 'bg-gray-50'), children: [messages.map(function (message) { return (_jsx("div", { className: "flex ".concat(message.role === 'user' ? 'justify-end' : 'justify-start'), children: _jsxs("div", { className: "max-w-[80%] ".concat(message.role === 'user' ? 'text-right' : 'text-left'), children: [_jsx("div", { className: "px-4 py-3 rounded-2xl text-sm leading-6 ".concat(message.role === 'user'
627
+ ? 'text-white'
628
+ : theme === 'dark'
629
+ ? 'bg-gray-700 text-gray-100 border border-gray-600 shadow-sm'
630
+ : 'bg-white text-gray-800 border border-gray-200 shadow-sm'), style: message.role === 'user' ? { backgroundImage: primaryColor } : {}, children: message.content }), showTimestamps && (_jsx("div", { className: "text-xs mt-1 ".concat(theme === 'dark' ? 'text-gray-500' : 'text-gray-400'), style: {
631
+ textAlign: message.role === 'user' ? 'right' : 'left',
632
+ paddingLeft: message.role === 'assistant' ? '16px' : '0',
633
+ paddingRight: message.role === 'user' ? '16px' : '0'
634
+ }, children: formatTimestamp(message.timestamp) }))] }) }, message.id)); }), isLoading && (_jsx("div", { className: "flex justify-start", children: _jsx("div", { className: "px-4 py-3 rounded-2xl max-w-[80%] text-sm leading-6 shadow-sm ".concat(theme === 'dark'
594
635
  ? 'bg-gray-700 text-gray-100 border border-gray-600'
595
636
  : 'bg-white text-gray-800 border border-gray-200'), children: _jsxs("div", { className: "flex space-x-1", children: [_jsx("div", { className: "w-2 h-2 rounded-full blumessage-animate-bounce ".concat(theme === 'dark' ? 'bg-gray-400' : 'bg-gray-400'), style: { animationDelay: '0ms' } }), _jsx("div", { className: "w-2 h-2 rounded-full blumessage-animate-bounce ".concat(theme === 'dark' ? 'bg-gray-400' : 'bg-gray-400'), style: { animationDelay: '150ms' } }), _jsx("div", { className: "w-2 h-2 rounded-full blumessage-animate-bounce ".concat(theme === 'dark' ? 'bg-gray-400' : 'bg-gray-400'), style: { animationDelay: '300ms' } })] }) }) })), messages.length === 0 && !isLoading && (_jsx("div", { className: "text-center text-sm py-8 ".concat(theme === 'dark' ? 'text-gray-400' : 'text-gray-500'), children: "No messages yet. Start a conversation!" })), _jsx("div", { ref: messagesEndRef })] }), _jsx("div", { className: "px-6 py-4 border-t ".concat(theme === 'dark'
596
637
  ? 'bg-gray-900 border-gray-700'
@@ -616,7 +657,12 @@ export var BlumessageChat = function (_a) {
616
657
  borderRadius: (fullScreen || isMaximized) ? '12px' : '24px'
617
658
  }, children: [_jsxs("div", { className: "flex items-center px-6 py-4 border-b ".concat(theme === 'dark'
618
659
  ? 'bg-gray-900 border-gray-700'
619
- : 'bg-white border-gray-100'), children: [_jsx("div", { className: "w-12 h-12 rounded-full bg-red-500 flex items-center justify-center mr-4", children: _jsx(AlertTriangle, { className: "w-6 h-6 text-white" }) }), _jsxs("div", { className: "flex-1", children: [_jsx("div", { className: "text-lg font-semibold m-0 leading-6 ".concat(theme === 'dark' ? 'text-gray-100' : 'text-gray-900'), children: "Connection Error" }), _jsx("div", { className: "text-sm m-0 leading-5 ".concat(theme === 'dark' ? 'text-gray-400' : 'text-gray-500'), children: "Unable to connect" })] }), floating && (_jsxs("div", { className: "flex items-center gap-1", children: [maximizeToggleButton && (_jsx("button", { onClick: handleToggleMaximize, className: "w-8 h-8 rounded-full flex items-center justify-center cursor-pointer transition-colors ".concat(theme === 'dark'
660
+ : 'bg-white border-gray-100'), children: [_jsx("div", { className: "rounded-full flex items-center justify-center", style: {
661
+ backgroundColor: '#ef4444',
662
+ width: '48px',
663
+ height: '48px',
664
+ marginRight: '24px'
665
+ }, children: _jsx(AlertTriangle, { className: "w-6 h-6 text-white" }) }), _jsxs("div", { className: "flex-1", children: [_jsx("div", { className: "text-lg font-semibold m-0 leading-6 ".concat(theme === 'dark' ? 'text-gray-100' : 'text-gray-900'), children: "Connection Error" }), _jsx("div", { className: "text-sm m-0 leading-5 ".concat(theme === 'dark' ? 'text-gray-400' : 'text-gray-500'), children: "Unable to connect" })] }), floating && (_jsxs("div", { className: "flex items-center gap-1", children: [maximizeToggleButton && (_jsx("button", { onClick: handleToggleMaximize, className: "w-8 h-8 rounded-full flex items-center justify-center cursor-pointer transition-colors ".concat(theme === 'dark'
620
666
  ? 'hover:bg-gray-800 text-gray-400 hover:text-gray-200'
621
667
  : 'hover:bg-gray-100 text-gray-500 hover:text-gray-700'), children: isMaximized ? _jsx(Minimize2, { className: "w-4 h-4" }) : _jsx(Maximize, { className: "w-4 h-4" }) })), _jsx("button", { onClick: handleCloseChat, className: "w-8 h-8 rounded-full flex items-center justify-center cursor-pointer transition-colors ".concat(theme === 'dark'
622
668
  ? 'hover:bg-gray-800 text-gray-400 hover:text-gray-200'
@@ -638,7 +684,12 @@ export var BlumessageChat = function (_a) {
638
684
  borderRadius: (fullScreen || isMaximized) ? '12px' : '24px'
639
685
  }, children: [_jsxs("div", { className: "flex items-center px-6 py-4 border-b ".concat(theme === 'dark'
640
686
  ? 'bg-gray-900 border-gray-700'
641
- : 'bg-white border-gray-100'), children: [_jsx("div", { className: "w-12 h-12 rounded-full flex items-center justify-center mr-4", style: { backgroundImage: primaryColor }, children: _jsx(Loader2, { className: "w-6 h-6 text-white animate-spin" }) }), _jsxs("div", { className: "flex-1", children: [_jsx("div", { className: "text-lg font-semibold m-0 leading-6 ".concat(theme === 'dark' ? 'text-gray-100' : 'text-gray-900'), children: name }), _jsx("div", { className: "text-sm m-0 leading-5 ".concat(theme === 'dark' ? 'text-gray-400' : 'text-gray-500'), children: subtitle })] }), floating && (_jsxs("div", { className: "flex items-center gap-1", children: [maximizeToggleButton && (_jsx("button", { onClick: handleToggleMaximize, className: "w-8 h-8 rounded-full flex items-center justify-center cursor-pointer transition-colors ".concat(theme === 'dark'
687
+ : 'bg-white border-gray-100'), children: [_jsx("div", { className: "rounded-full flex items-center justify-center", style: {
688
+ backgroundImage: primaryColor,
689
+ width: '48px',
690
+ height: '48px',
691
+ marginRight: '24px'
692
+ }, children: _jsx(Loader2, { className: "w-6 h-6 text-white animate-spin" }) }), _jsxs("div", { className: "flex-1", children: [_jsx("div", { className: "text-lg font-semibold m-0 leading-6 ".concat(theme === 'dark' ? 'text-gray-100' : 'text-gray-900'), children: name }), _jsx("div", { className: "text-sm m-0 leading-5 ".concat(theme === 'dark' ? 'text-gray-400' : 'text-gray-500'), children: subtitle })] }), floating && (_jsxs("div", { className: "flex items-center gap-1", children: [maximizeToggleButton && (_jsx("button", { onClick: handleToggleMaximize, className: "w-8 h-8 rounded-full flex items-center justify-center cursor-pointer transition-colors ".concat(theme === 'dark'
642
693
  ? 'hover:bg-gray-800 text-gray-400 hover:text-gray-200'
643
694
  : 'hover:bg-gray-100 text-gray-500 hover:text-gray-700'), children: isMaximized ? _jsx(Minimize2, { className: "w-4 h-4" }) : _jsx(Maximize, { className: "w-4 h-4" }) })), _jsx("button", { onClick: handleCloseChat, className: "w-8 h-8 rounded-full flex items-center justify-center cursor-pointer transition-colors ".concat(theme === 'dark'
644
695
  ? 'hover:bg-gray-800 text-gray-400 hover:text-gray-200'
@@ -35,6 +35,7 @@ export interface BlumessageChatProps {
35
35
  onChatWidgetClosed?: () => void;
36
36
  onError?: (error: string, context?: string) => void;
37
37
  persistent?: boolean;
38
+ showTimestamps?: boolean;
38
39
  floating?: boolean;
39
40
  buttonText?: string;
40
41
  buttonPosition?: 'bottom-right' | 'bottom-left' | 'top-right' | 'top-left';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@blumessage/react-chat",
3
- "version": "1.0.6",
3
+ "version": "1.0.7",
4
4
  "description": "A React TypeScript chat widget component with floating button, theming, and Blumessage API integration",
5
5
  "license": "MIT",
6
6
  "author": "Blumessage <contact@blumessage.com>",