@blumessage/react-chat 1.8.2 → 1.8.3
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/package.json +1 -1
- package/dist/BlumessageChat.js +0 -1150
- package/dist/index.browser.js +0 -7
- package/dist/index.commonjs.js +0 -15
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@blumessage/react-chat",
|
|
3
|
-
"version": "1.8.
|
|
3
|
+
"version": "1.8.3",
|
|
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>",
|
package/dist/BlumessageChat.js
DELETED
|
@@ -1,1150 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
-
if (k2 === undefined) k2 = k;
|
|
4
|
-
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
-
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
-
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
-
}
|
|
8
|
-
Object.defineProperty(o, k2, desc);
|
|
9
|
-
}) : (function(o, m, k, k2) {
|
|
10
|
-
if (k2 === undefined) k2 = k;
|
|
11
|
-
o[k2] = m[k];
|
|
12
|
-
}));
|
|
13
|
-
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
-
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
-
}) : function(o, v) {
|
|
16
|
-
o["default"] = v;
|
|
17
|
-
});
|
|
18
|
-
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
-
var ownKeys = function(o) {
|
|
20
|
-
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
-
var ar = [];
|
|
22
|
-
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
-
return ar;
|
|
24
|
-
};
|
|
25
|
-
return ownKeys(o);
|
|
26
|
-
};
|
|
27
|
-
return function (mod) {
|
|
28
|
-
if (mod && mod.__esModule) return mod;
|
|
29
|
-
var result = {};
|
|
30
|
-
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
-
__setModuleDefault(result, mod);
|
|
32
|
-
return result;
|
|
33
|
-
};
|
|
34
|
-
})();
|
|
35
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
36
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
37
|
-
};
|
|
38
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
|
-
exports.BlumessageChat = void 0;
|
|
40
|
-
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
41
|
-
const react_1 = __importStar(require("react"));
|
|
42
|
-
const react_markdown_1 = __importDefault(require("react-markdown"));
|
|
43
|
-
const remark_gfm_1 = __importDefault(require("remark-gfm"));
|
|
44
|
-
const LucideIcons = __importStar(require("lucide-react"));
|
|
45
|
-
require("./styles.css");
|
|
46
|
-
// Custom CSS animations that don't depend on Tailwind
|
|
47
|
-
const customStyles = `
|
|
48
|
-
@keyframes bounce {
|
|
49
|
-
0%, 100% {
|
|
50
|
-
transform: translateY(0);
|
|
51
|
-
}
|
|
52
|
-
50% {
|
|
53
|
-
transform: translateY(-25%);
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
@keyframes spin {
|
|
58
|
-
0% {
|
|
59
|
-
transform: rotate(0deg);
|
|
60
|
-
}
|
|
61
|
-
100% {
|
|
62
|
-
transform: rotate(360deg);
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
@keyframes helpBubbleIn {
|
|
67
|
-
0% {
|
|
68
|
-
transform: translateY(20px) scale(0.8);
|
|
69
|
-
opacity: 0;
|
|
70
|
-
}
|
|
71
|
-
100% {
|
|
72
|
-
transform: translateY(0) scale(1);
|
|
73
|
-
opacity: 1;
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
@keyframes helpBubbleOut {
|
|
78
|
-
0% {
|
|
79
|
-
transform: translateY(0) scale(1);
|
|
80
|
-
opacity: 1;
|
|
81
|
-
}
|
|
82
|
-
100% {
|
|
83
|
-
transform: translateY(20px) scale(0.8);
|
|
84
|
-
opacity: 0;
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
@keyframes helpBubbleInTop {
|
|
89
|
-
0% {
|
|
90
|
-
transform: translateY(-20px) scale(0.8);
|
|
91
|
-
opacity: 0;
|
|
92
|
-
}
|
|
93
|
-
100% {
|
|
94
|
-
transform: translateY(0) scale(1);
|
|
95
|
-
opacity: 1;
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
@keyframes helpBubbleOutTop {
|
|
100
|
-
0% {
|
|
101
|
-
transform: translateY(0) scale(1);
|
|
102
|
-
opacity: 1;
|
|
103
|
-
}
|
|
104
|
-
100% {
|
|
105
|
-
transform: translateY(-20px) scale(0.8);
|
|
106
|
-
opacity: 0;
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
.blumessage-animate-bounce {
|
|
111
|
-
animation: bounce 1s infinite;
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
.blumessage-animate-spin {
|
|
115
|
-
animation: spin 1s linear infinite;
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
.blumessage-help-bubble-in {
|
|
119
|
-
animation: helpBubbleIn 0.3s cubic-bezier(0.4, 0, 0.2, 1) forwards;
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
.blumessage-help-bubble-out {
|
|
123
|
-
animation: helpBubbleOut 0.3s cubic-bezier(0.4, 0, 0.2, 1) forwards;
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
.blumessage-help-bubble-in-top {
|
|
127
|
-
animation: helpBubbleInTop 0.3s cubic-bezier(0.4, 0, 0.2, 1) forwards;
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
.blumessage-help-bubble-out-top {
|
|
131
|
-
animation: helpBubbleOutTop 0.3s cubic-bezier(0.4, 0, 0.2, 1) forwards;
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
/* Mobile-specific styles */
|
|
135
|
-
@media (max-width: 768px) {
|
|
136
|
-
.blumessage-mobile-fullscreen {
|
|
137
|
-
position: fixed !important;
|
|
138
|
-
top: 0 !important;
|
|
139
|
-
left: 0 !important;
|
|
140
|
-
right: 0 !important;
|
|
141
|
-
bottom: 0 !important;
|
|
142
|
-
width: 100% !important;
|
|
143
|
-
height: 100% !important;
|
|
144
|
-
max-width: 100% !important;
|
|
145
|
-
max-height: 100% !important;
|
|
146
|
-
border-radius: 0 !important;
|
|
147
|
-
z-index: 9999 !important;
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
.blumessage-mobile-chat {
|
|
151
|
-
border-radius: 16px !important;
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
.blumessage-mobile-button {
|
|
155
|
-
position: fixed !important;
|
|
156
|
-
bottom: 20px !important;
|
|
157
|
-
right: 20px !important;
|
|
158
|
-
left: auto !important;
|
|
159
|
-
top: auto !important;
|
|
160
|
-
}
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
@media (max-width: 480px) {
|
|
164
|
-
.blumessage-mobile-fullscreen {
|
|
165
|
-
border-radius: 0 !important;
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
.blumessage-mobile-chat {
|
|
169
|
-
border-radius: 12px !important;
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
.blumessage-mobile-button {
|
|
173
|
-
bottom: 16px !important;
|
|
174
|
-
right: 16px !important;
|
|
175
|
-
}
|
|
176
|
-
}
|
|
177
|
-
`;
|
|
178
|
-
// Inject custom styles
|
|
179
|
-
if (typeof document !== 'undefined' && !document.getElementById('blumessage-styles')) {
|
|
180
|
-
const style = document.createElement('style');
|
|
181
|
-
style.id = 'blumessage-styles';
|
|
182
|
-
style.textContent = customStyles;
|
|
183
|
-
document.head.appendChild(style);
|
|
184
|
-
}
|
|
185
|
-
// Session storage key for conversation token
|
|
186
|
-
const CONVERSATION_TOKEN_KEY = 'blumessage_conversation_token';
|
|
187
|
-
// Utility functions for storage
|
|
188
|
-
const saveConversationToken = (token, persistent = false) => {
|
|
189
|
-
try {
|
|
190
|
-
const storage = persistent ? localStorage : sessionStorage;
|
|
191
|
-
storage.setItem(CONVERSATION_TOKEN_KEY, token);
|
|
192
|
-
}
|
|
193
|
-
catch (error) {
|
|
194
|
-
// Silent fail - storage might not be available
|
|
195
|
-
}
|
|
196
|
-
};
|
|
197
|
-
const getStoredConversationToken = (persistent = false) => {
|
|
198
|
-
try {
|
|
199
|
-
const storage = persistent ? localStorage : sessionStorage;
|
|
200
|
-
return storage.getItem(CONVERSATION_TOKEN_KEY);
|
|
201
|
-
}
|
|
202
|
-
catch (error) {
|
|
203
|
-
// Silent fail - storage might not be available
|
|
204
|
-
return null;
|
|
205
|
-
}
|
|
206
|
-
};
|
|
207
|
-
const clearStoredConversationToken = (persistent = false) => {
|
|
208
|
-
try {
|
|
209
|
-
const storage = persistent ? localStorage : sessionStorage;
|
|
210
|
-
storage.removeItem(CONVERSATION_TOKEN_KEY);
|
|
211
|
-
}
|
|
212
|
-
catch (error) {
|
|
213
|
-
// Silent fail - storage might not be available
|
|
214
|
-
}
|
|
215
|
-
};
|
|
216
|
-
// Custom hook for mobile detection
|
|
217
|
-
const useIsMobile = () => {
|
|
218
|
-
const [isMobile, setIsMobile] = (0, react_1.useState)(false);
|
|
219
|
-
(0, react_1.useEffect)(() => {
|
|
220
|
-
const checkIsMobile = () => {
|
|
221
|
-
const userAgent = navigator.userAgent || navigator.vendor || window.opera;
|
|
222
|
-
const mobileRegex = /android|webos|iphone|ipad|ipod|blackberry|iemobile|opera mini/i;
|
|
223
|
-
const isMobileDevice = mobileRegex.test(userAgent.toLowerCase());
|
|
224
|
-
const isSmallScreen = window.innerWidth <= 768;
|
|
225
|
-
setIsMobile(isMobileDevice || isSmallScreen);
|
|
226
|
-
};
|
|
227
|
-
checkIsMobile();
|
|
228
|
-
window.addEventListener('resize', checkIsMobile);
|
|
229
|
-
return () => window.removeEventListener('resize', checkIsMobile);
|
|
230
|
-
}, []);
|
|
231
|
-
return isMobile;
|
|
232
|
-
};
|
|
233
|
-
exports.BlumessageChat = (0, react_1.forwardRef)(({ apiKey, placeholder = "Type your message...", theme = 'light', width, height, size = 'medium', name = "Blumessage AI", subtitle = "Online • Instant responses", initialMessages = [], onUserMessage, onAssistantMessage, token: initialToken, onTokenChange, onChatWidgetOpen, onChatWidgetClosed, onError, persistent = false, showTimestamps = false, typingText = "Agent is typing...", emptyStateText = "Start a conversation!", markdown = true, disableAutoScroll = false,
|
|
234
|
-
// Floating button props
|
|
235
|
-
floating = true, buttonText = "Chat with us", buttonPosition = 'bottom-right', buttonStyle, defaultOpen = false, maximizeToggleButton = true, fullScreen = false, icon = 'message-circle',
|
|
236
|
-
// Styling props
|
|
237
|
-
primaryColor = "linear-gradient(to right, #3b82f6,rgb(8, 98, 242))",
|
|
238
|
-
// Help bubble props
|
|
239
|
-
showHelpBubble = true, helpBubbleMessage = "Have a question? We're here to help!", helpBubbleIcon = 'message-circle-question-mark', helpBubbleIconName, helpBubbleShowAfter = 1, // 1 second
|
|
240
|
-
helpBubbleHideAfter = 5, // 5 seconds
|
|
241
|
-
helpBubbleBackgroundColor = "#000000", context, }, ref) => {
|
|
242
|
-
const [isInitialized, setIsInitialized] = (0, react_1.useState)(false);
|
|
243
|
-
const [error, setError] = (0, react_1.useState)(null);
|
|
244
|
-
const [messages, setMessages] = (0, react_1.useState)(initialMessages);
|
|
245
|
-
const [inputValue, setInputValue] = (0, react_1.useState)('');
|
|
246
|
-
const [token, setToken] = (0, react_1.useState)(initialToken || getStoredConversationToken(persistent));
|
|
247
|
-
const [isOpen, setIsOpen] = (0, react_1.useState)(defaultOpen);
|
|
248
|
-
const [isAnimating, setIsAnimating] = (0, react_1.useState)(false);
|
|
249
|
-
const [isLoading, setIsLoading] = (0, react_1.useState)(false);
|
|
250
|
-
const [isMaximized, setIsMaximized] = (0, react_1.useState)(false);
|
|
251
|
-
const messagesEndRef = (0, react_1.useRef)(null);
|
|
252
|
-
const isInitialLoad = (0, react_1.useRef)(true);
|
|
253
|
-
const [isHelpBubbleVisible, setIsHelpBubbleVisible] = (0, react_1.useState)(false);
|
|
254
|
-
const [isHelpBubbleAnimating, setIsHelpBubbleAnimating] = (0, react_1.useState)(false);
|
|
255
|
-
// Mobile detection
|
|
256
|
-
const isMobile = useIsMobile();
|
|
257
|
-
// Help bubble effect
|
|
258
|
-
(0, react_1.useEffect)(() => {
|
|
259
|
-
if (!floating || !showHelpBubble)
|
|
260
|
-
return;
|
|
261
|
-
const showTimer = setTimeout(() => {
|
|
262
|
-
setIsHelpBubbleAnimating(true);
|
|
263
|
-
setIsHelpBubbleVisible(true);
|
|
264
|
-
// Remove animating state after animation completes
|
|
265
|
-
setTimeout(() => {
|
|
266
|
-
setIsHelpBubbleAnimating(false);
|
|
267
|
-
}, 300);
|
|
268
|
-
// Hide the bubble after the specified time if hideAfter > 0
|
|
269
|
-
if (helpBubbleHideAfter > 0) {
|
|
270
|
-
const hideTimer = setTimeout(() => {
|
|
271
|
-
setIsHelpBubbleAnimating(true);
|
|
272
|
-
setIsHelpBubbleVisible(false);
|
|
273
|
-
// Remove animating state after animation completes
|
|
274
|
-
setTimeout(() => {
|
|
275
|
-
setIsHelpBubbleAnimating(false);
|
|
276
|
-
}, 300);
|
|
277
|
-
}, helpBubbleHideAfter * 1000);
|
|
278
|
-
return () => clearTimeout(hideTimer);
|
|
279
|
-
}
|
|
280
|
-
}, helpBubbleShowAfter * 1000);
|
|
281
|
-
return () => clearTimeout(showTimer);
|
|
282
|
-
}, [floating, showHelpBubble, helpBubbleShowAfter, helpBubbleHideAfter]);
|
|
283
|
-
// Helper function to format timestamp
|
|
284
|
-
const formatTimestamp = (timestamp) => {
|
|
285
|
-
const date = new Date(timestamp);
|
|
286
|
-
const now = new Date();
|
|
287
|
-
// Check if the message is from today
|
|
288
|
-
const isToday = date.toDateString() === now.toDateString();
|
|
289
|
-
if (isToday) {
|
|
290
|
-
// If today: show time only in user's locale format (14:00 or 2:00 PM)
|
|
291
|
-
return date.toLocaleTimeString(undefined, {
|
|
292
|
-
hour: '2-digit',
|
|
293
|
-
minute: '2-digit',
|
|
294
|
-
hour12: undefined // Let browser decide based on user's locale
|
|
295
|
-
});
|
|
296
|
-
}
|
|
297
|
-
else {
|
|
298
|
-
// If more than a day: show "17 July, 13:00" format in user's locale
|
|
299
|
-
const dateStr = date.toLocaleDateString(undefined, {
|
|
300
|
-
day: 'numeric',
|
|
301
|
-
month: 'long'
|
|
302
|
-
});
|
|
303
|
-
const timeStr = date.toLocaleTimeString(undefined, {
|
|
304
|
-
hour: '2-digit',
|
|
305
|
-
minute: '2-digit',
|
|
306
|
-
hour12: undefined // Let browser decide based on user's locale
|
|
307
|
-
});
|
|
308
|
-
return `${dateStr}, ${timeStr}`;
|
|
309
|
-
}
|
|
310
|
-
};
|
|
311
|
-
// Helper function to get dimensions based on size
|
|
312
|
-
const getDimensions = () => {
|
|
313
|
-
// If custom width/height are provided, use them
|
|
314
|
-
if (width && height) {
|
|
315
|
-
return { width, height };
|
|
316
|
-
}
|
|
317
|
-
// Mobile-first responsive dimensions
|
|
318
|
-
if (isMobile) {
|
|
319
|
-
// On mobile, use fullscreen or near-fullscreen dimensions
|
|
320
|
-
if (floating) {
|
|
321
|
-
return {
|
|
322
|
-
width: 'calc(100vw - 32px)',
|
|
323
|
-
height: 'calc(100vh - 100px)'
|
|
324
|
-
};
|
|
325
|
-
}
|
|
326
|
-
else {
|
|
327
|
-
return {
|
|
328
|
-
width: '100%',
|
|
329
|
-
height: '500px'
|
|
330
|
-
};
|
|
331
|
-
}
|
|
332
|
-
}
|
|
333
|
-
// Desktop dimensions based on size
|
|
334
|
-
let dimensions;
|
|
335
|
-
switch (size) {
|
|
336
|
-
case 'small':
|
|
337
|
-
dimensions = { width: '320px', height: '400px' };
|
|
338
|
-
break;
|
|
339
|
-
case 'large':
|
|
340
|
-
dimensions = { width: '680px', height: '800px' };
|
|
341
|
-
break;
|
|
342
|
-
case 'medium':
|
|
343
|
-
default:
|
|
344
|
-
dimensions = { width: '600px', height: '600px' };
|
|
345
|
-
}
|
|
346
|
-
return dimensions;
|
|
347
|
-
};
|
|
348
|
-
const { width: actualWidth, height: actualHeight } = getDimensions();
|
|
349
|
-
// Function to clear conversation and start fresh
|
|
350
|
-
const clearConversation = () => {
|
|
351
|
-
updateConversationToken(null);
|
|
352
|
-
setMessages([]);
|
|
353
|
-
};
|
|
354
|
-
// Function to update conversation token and notify parent
|
|
355
|
-
const updateConversationToken = (newToken) => {
|
|
356
|
-
setToken(newToken);
|
|
357
|
-
if (newToken) {
|
|
358
|
-
saveConversationToken(newToken, persistent);
|
|
359
|
-
}
|
|
360
|
-
else {
|
|
361
|
-
clearStoredConversationToken(persistent);
|
|
362
|
-
}
|
|
363
|
-
if (onTokenChange) {
|
|
364
|
-
onTokenChange(newToken);
|
|
365
|
-
}
|
|
366
|
-
};
|
|
367
|
-
// Function to fetch conversation history
|
|
368
|
-
const fetchConversationHistory = async (convToken) => {
|
|
369
|
-
try {
|
|
370
|
-
const response = await fetch(`https://api.blumessage.com/api/v1/conversations/session/${convToken}`, {
|
|
371
|
-
method: 'GET',
|
|
372
|
-
headers: {
|
|
373
|
-
'Authorization': `Bearer ${apiKey}`,
|
|
374
|
-
},
|
|
375
|
-
});
|
|
376
|
-
if (response.ok) {
|
|
377
|
-
const sessionData = await response.json();
|
|
378
|
-
return sessionData.messages || [];
|
|
379
|
-
}
|
|
380
|
-
else {
|
|
381
|
-
// Clear invalid conversation ID from storage
|
|
382
|
-
updateConversationToken(null);
|
|
383
|
-
if (onError) {
|
|
384
|
-
onError(`Failed to fetch conversation history: ${response.status} ${response.statusText}`, "conversation_history");
|
|
385
|
-
}
|
|
386
|
-
return [];
|
|
387
|
-
}
|
|
388
|
-
}
|
|
389
|
-
catch (error) {
|
|
390
|
-
// Clear invalid conversation ID from storage
|
|
391
|
-
updateConversationToken(null);
|
|
392
|
-
if (onError) {
|
|
393
|
-
onError(`Error fetching conversation history: ${error}`, "conversation_history");
|
|
394
|
-
}
|
|
395
|
-
return [];
|
|
396
|
-
}
|
|
397
|
-
};
|
|
398
|
-
(0, react_1.useEffect)(() => {
|
|
399
|
-
if (!apiKey) {
|
|
400
|
-
const errorMessage = "API key is required";
|
|
401
|
-
setError(errorMessage);
|
|
402
|
-
if (onError) {
|
|
403
|
-
onError(errorMessage, "missing_api_key");
|
|
404
|
-
}
|
|
405
|
-
return;
|
|
406
|
-
}
|
|
407
|
-
// Validate API key with Blumessage API
|
|
408
|
-
const validateApiKey = async () => {
|
|
409
|
-
try {
|
|
410
|
-
setIsInitialized(false);
|
|
411
|
-
setError(null);
|
|
412
|
-
const response = await fetch('https://api.blumessage.com/api/v1/api-keys/validate', {
|
|
413
|
-
method: 'POST',
|
|
414
|
-
headers: {
|
|
415
|
-
'Content-Type': 'application/json',
|
|
416
|
-
},
|
|
417
|
-
body: JSON.stringify({ apiKey }),
|
|
418
|
-
});
|
|
419
|
-
if (response.ok) {
|
|
420
|
-
setIsInitialized(true);
|
|
421
|
-
setError(null);
|
|
422
|
-
}
|
|
423
|
-
else {
|
|
424
|
-
const errorMessage = "Unable to connect - invalid API key";
|
|
425
|
-
setError(errorMessage);
|
|
426
|
-
setIsInitialized(false);
|
|
427
|
-
if (onError) {
|
|
428
|
-
onError(errorMessage, "api_key_validation");
|
|
429
|
-
}
|
|
430
|
-
}
|
|
431
|
-
}
|
|
432
|
-
catch (err) {
|
|
433
|
-
const errorMessage = "Unable to connect - network error";
|
|
434
|
-
setError(errorMessage);
|
|
435
|
-
setIsInitialized(false);
|
|
436
|
-
if (onError) {
|
|
437
|
-
onError(errorMessage, "network_error");
|
|
438
|
-
}
|
|
439
|
-
}
|
|
440
|
-
};
|
|
441
|
-
validateApiKey();
|
|
442
|
-
}, [apiKey]);
|
|
443
|
-
// Handle initial message loading (only runs once on mount)
|
|
444
|
-
(0, react_1.useEffect)(() => {
|
|
445
|
-
const loadInitialMessages = async () => {
|
|
446
|
-
if (isInitialLoad.current) {
|
|
447
|
-
isInitialLoad.current = false;
|
|
448
|
-
// Handle message initialization based on conversation state
|
|
449
|
-
if (initialMessages.length > 0 && token) {
|
|
450
|
-
// Warn if both are provided - initialMessages takes precedence
|
|
451
|
-
setMessages(initialMessages);
|
|
452
|
-
}
|
|
453
|
-
else if (initialMessages.length > 0) {
|
|
454
|
-
// Use provided initial messages
|
|
455
|
-
setMessages(initialMessages);
|
|
456
|
-
}
|
|
457
|
-
else if (token) {
|
|
458
|
-
// Fetch conversation history if we have a stored conversation ID
|
|
459
|
-
const historyMessages = await fetchConversationHistory(token);
|
|
460
|
-
setMessages(historyMessages); // Will be empty array if fetch failed
|
|
461
|
-
}
|
|
462
|
-
else {
|
|
463
|
-
// No conversation ID and no initial messages - start with empty array
|
|
464
|
-
setMessages([]);
|
|
465
|
-
}
|
|
466
|
-
}
|
|
467
|
-
};
|
|
468
|
-
// Only load initial messages after API key is validated
|
|
469
|
-
if (isInitialized) {
|
|
470
|
-
loadInitialMessages();
|
|
471
|
-
}
|
|
472
|
-
}, [isInitialized]);
|
|
473
|
-
const handleSendMessage = async () => {
|
|
474
|
-
if (!inputValue.trim() || isLoading)
|
|
475
|
-
return;
|
|
476
|
-
const userMessage = {
|
|
477
|
-
id: Date.now().toString(),
|
|
478
|
-
content: inputValue.trim(),
|
|
479
|
-
role: 'user',
|
|
480
|
-
timestamp: Date.now()
|
|
481
|
-
};
|
|
482
|
-
// Add user message to UI immediately
|
|
483
|
-
setMessages(prev => [...prev, userMessage]);
|
|
484
|
-
const currentInput = inputValue.trim();
|
|
485
|
-
setInputValue('');
|
|
486
|
-
setIsLoading(true);
|
|
487
|
-
// Call the callback if provided
|
|
488
|
-
if (onUserMessage) {
|
|
489
|
-
onUserMessage(userMessage);
|
|
490
|
-
}
|
|
491
|
-
try {
|
|
492
|
-
// Call Blumessage API
|
|
493
|
-
const requestBody = {
|
|
494
|
-
message: currentInput,
|
|
495
|
-
};
|
|
496
|
-
// Include token if we have one
|
|
497
|
-
if (token) {
|
|
498
|
-
requestBody.token = token;
|
|
499
|
-
}
|
|
500
|
-
// Include user context if provided
|
|
501
|
-
if (context && Object.keys(context).length > 0) {
|
|
502
|
-
requestBody.context = context;
|
|
503
|
-
}
|
|
504
|
-
const response = await fetch('https://api.blumessage.com/api/v1/conversations', {
|
|
505
|
-
method: 'POST',
|
|
506
|
-
headers: {
|
|
507
|
-
'Content-Type': 'application/json',
|
|
508
|
-
'Authorization': `Bearer ${apiKey}`,
|
|
509
|
-
},
|
|
510
|
-
body: JSON.stringify(requestBody),
|
|
511
|
-
});
|
|
512
|
-
if (response.ok) {
|
|
513
|
-
const apiResponse = await response.json();
|
|
514
|
-
// Update token from response (for both first message and continuing conversations)
|
|
515
|
-
updateConversationToken(apiResponse.token);
|
|
516
|
-
// Find the assistant's response (the last message that's not from user)
|
|
517
|
-
const assistantMessages = apiResponse.messages.filter(msg => msg.role === 'assistant');
|
|
518
|
-
const latestAssistantMessage = assistantMessages[assistantMessages.length - 1];
|
|
519
|
-
if (latestAssistantMessage) {
|
|
520
|
-
// Add assistant's response to messages
|
|
521
|
-
const assistantResponse = {
|
|
522
|
-
id: (Date.now() + 1).toString(),
|
|
523
|
-
content: latestAssistantMessage.content,
|
|
524
|
-
role: 'assistant',
|
|
525
|
-
timestamp: latestAssistantMessage.timestamp,
|
|
526
|
-
};
|
|
527
|
-
setMessages(prev => [...prev, assistantResponse]);
|
|
528
|
-
if (onAssistantMessage) {
|
|
529
|
-
onAssistantMessage(assistantResponse);
|
|
530
|
-
}
|
|
531
|
-
}
|
|
532
|
-
}
|
|
533
|
-
else {
|
|
534
|
-
// If token is invalid, clear it
|
|
535
|
-
if (response.status === 401 || response.status === 403) {
|
|
536
|
-
updateConversationToken(null);
|
|
537
|
-
}
|
|
538
|
-
// Add error message
|
|
539
|
-
const errorMessage = {
|
|
540
|
-
id: (Date.now() + 1).toString(),
|
|
541
|
-
content: "Sorry, I'm having trouble connecting right now. Please try again.",
|
|
542
|
-
role: 'assistant',
|
|
543
|
-
timestamp: Date.now(),
|
|
544
|
-
};
|
|
545
|
-
setMessages(prev => [...prev, errorMessage]);
|
|
546
|
-
if (onError) {
|
|
547
|
-
onError(`Failed to send message: ${response.status} ${response.statusText}`, "message_send");
|
|
548
|
-
}
|
|
549
|
-
}
|
|
550
|
-
}
|
|
551
|
-
catch (error) {
|
|
552
|
-
// Add error message
|
|
553
|
-
const errorMessage = {
|
|
554
|
-
id: (Date.now() + 1).toString(),
|
|
555
|
-
content: "Sorry, I'm having trouble connecting right now. Please try again.",
|
|
556
|
-
role: 'assistant',
|
|
557
|
-
timestamp: Date.now(),
|
|
558
|
-
};
|
|
559
|
-
setMessages(prev => [...prev, errorMessage]);
|
|
560
|
-
if (onError) {
|
|
561
|
-
onError(`Error sending message: ${error}`, "message_send");
|
|
562
|
-
}
|
|
563
|
-
}
|
|
564
|
-
finally {
|
|
565
|
-
setIsLoading(false);
|
|
566
|
-
}
|
|
567
|
-
};
|
|
568
|
-
const handleKeyPress = (e) => {
|
|
569
|
-
if (e.key === 'Enter' && !e.shiftKey && !isLoading) {
|
|
570
|
-
e.preventDefault();
|
|
571
|
-
handleSendMessage();
|
|
572
|
-
}
|
|
573
|
-
};
|
|
574
|
-
// Auto-scroll to bottom when messages change
|
|
575
|
-
(0, react_1.useEffect)(() => {
|
|
576
|
-
if (!disableAutoScroll) {
|
|
577
|
-
messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' });
|
|
578
|
-
}
|
|
579
|
-
}, [messages]);
|
|
580
|
-
// Auto-scroll to bottom when chat widget opens
|
|
581
|
-
(0, react_1.useEffect)(() => {
|
|
582
|
-
if (isOpen && floating && !disableAutoScroll) {
|
|
583
|
-
// Add a small delay to ensure the DOM is updated after the animation starts
|
|
584
|
-
setTimeout(() => {
|
|
585
|
-
messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' });
|
|
586
|
-
}, 50);
|
|
587
|
-
}
|
|
588
|
-
}, [isOpen, floating, disableAutoScroll]);
|
|
589
|
-
// Handle opening the chat with animation
|
|
590
|
-
const handleOpenChat = () => {
|
|
591
|
-
if (!isOpen && !isAnimating) {
|
|
592
|
-
setIsAnimating(true);
|
|
593
|
-
// Small delay to ensure smooth animation start
|
|
594
|
-
setTimeout(() => {
|
|
595
|
-
setIsOpen(true);
|
|
596
|
-
// Call the callback after the chat is open
|
|
597
|
-
if (onChatWidgetOpen) {
|
|
598
|
-
onChatWidgetOpen();
|
|
599
|
-
}
|
|
600
|
-
}, 10);
|
|
601
|
-
// Animation duration
|
|
602
|
-
setTimeout(() => {
|
|
603
|
-
setIsAnimating(false);
|
|
604
|
-
}, 300);
|
|
605
|
-
}
|
|
606
|
-
};
|
|
607
|
-
// Handle closing the chat with animation
|
|
608
|
-
const handleCloseChat = () => {
|
|
609
|
-
if (isOpen && !isAnimating) {
|
|
610
|
-
setIsAnimating(true);
|
|
611
|
-
setIsOpen(false);
|
|
612
|
-
// Reset maximized state when closing
|
|
613
|
-
setIsMaximized(false);
|
|
614
|
-
// Call the callback when closing
|
|
615
|
-
if (onChatWidgetClosed) {
|
|
616
|
-
onChatWidgetClosed();
|
|
617
|
-
}
|
|
618
|
-
// Wait for animation to complete
|
|
619
|
-
setTimeout(() => {
|
|
620
|
-
setIsAnimating(false);
|
|
621
|
-
}, 300);
|
|
622
|
-
}
|
|
623
|
-
};
|
|
624
|
-
// Handle maximize/minimize toggle
|
|
625
|
-
const handleToggleMaximize = () => {
|
|
626
|
-
setIsMaximized(!isMaximized);
|
|
627
|
-
};
|
|
628
|
-
// Helper function to get any Lucide React icon by name
|
|
629
|
-
const getLucideIcon = (iconName) => {
|
|
630
|
-
if (!iconName)
|
|
631
|
-
return LucideIcons.MessageCircle;
|
|
632
|
-
// Convert icon name to PascalCase (e.g., "message-circle" -> "MessageCircle")
|
|
633
|
-
const pascalCaseName = iconName
|
|
634
|
-
.split(/[-_\s]+/)
|
|
635
|
-
.map(word => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase())
|
|
636
|
-
.join('');
|
|
637
|
-
// Return the icon component if it exists, otherwise fallback to MessageCircle
|
|
638
|
-
const IconComponent = LucideIcons[pascalCaseName];
|
|
639
|
-
if (!IconComponent) {
|
|
640
|
-
}
|
|
641
|
-
return (IconComponent || LucideIcons.MessageCircle);
|
|
642
|
-
};
|
|
643
|
-
// Get the help bubble icon component based on the helpBubbleIcon or helpBubbleIconName prop
|
|
644
|
-
const getHelpBubbleIconComponent = () => {
|
|
645
|
-
// If helpBubbleIconName is provided, use it directly
|
|
646
|
-
if (helpBubbleIconName) {
|
|
647
|
-
return getLucideIcon(helpBubbleIconName);
|
|
648
|
-
}
|
|
649
|
-
// Otherwise use the helpBubbleIcon prop
|
|
650
|
-
return getLucideIcon(helpBubbleIcon);
|
|
651
|
-
};
|
|
652
|
-
// Helper function to determine if a color is dark (for text contrast)
|
|
653
|
-
const isColorDark = (color) => {
|
|
654
|
-
// Handle hex colors
|
|
655
|
-
if (color.startsWith('#')) {
|
|
656
|
-
const hex = color.replace('#', '');
|
|
657
|
-
const r = parseInt(hex.substr(0, 2), 16);
|
|
658
|
-
const g = parseInt(hex.substr(2, 2), 16);
|
|
659
|
-
const b = parseInt(hex.substr(4, 2), 16);
|
|
660
|
-
// Calculate luminance
|
|
661
|
-
const luminance = (0.299 * r + 0.587 * g + 0.114 * b) / 255;
|
|
662
|
-
return luminance < 0.5;
|
|
663
|
-
}
|
|
664
|
-
// Handle rgb/rgba colors
|
|
665
|
-
if (color.startsWith('rgb')) {
|
|
666
|
-
const match = color.match(/\d+/g);
|
|
667
|
-
if (match && match.length >= 3) {
|
|
668
|
-
const r = parseInt(match[0]);
|
|
669
|
-
const g = parseInt(match[1]);
|
|
670
|
-
const b = parseInt(match[2]);
|
|
671
|
-
const luminance = (0.299 * r + 0.587 * g + 0.114 * b) / 255;
|
|
672
|
-
return luminance < 0.5;
|
|
673
|
-
}
|
|
674
|
-
}
|
|
675
|
-
// Default to assuming light color for unknown formats
|
|
676
|
-
return false;
|
|
677
|
-
};
|
|
678
|
-
// Helper function to get appropriate text color based on background
|
|
679
|
-
const getTextColor = (backgroundColor, theme) => {
|
|
680
|
-
// If custom background color is provided, determine text color based on brightness
|
|
681
|
-
if (backgroundColor && backgroundColor !== '#ffffff' && backgroundColor !== '#1f2937') {
|
|
682
|
-
return isColorDark(backgroundColor) ? 'blumessage-text-white' : 'blumessage-text-gray-800';
|
|
683
|
-
}
|
|
684
|
-
// Fall back to theme-based colors
|
|
685
|
-
return theme === 'dark' ? 'blumessage-text-gray-100' : 'blumessage-text-gray-800';
|
|
686
|
-
};
|
|
687
|
-
// Get the icon component based on the icon prop
|
|
688
|
-
const getIconComponent = () => {
|
|
689
|
-
return getLucideIcon(icon);
|
|
690
|
-
};
|
|
691
|
-
// Expose methods to parent component via ref
|
|
692
|
-
(0, react_1.useImperativeHandle)(ref, () => ({
|
|
693
|
-
sendMessage: async (message) => {
|
|
694
|
-
if (!message.trim() || isLoading)
|
|
695
|
-
return;
|
|
696
|
-
const userMessage = {
|
|
697
|
-
id: Date.now().toString(),
|
|
698
|
-
content: message.trim(),
|
|
699
|
-
role: 'user',
|
|
700
|
-
timestamp: Date.now()
|
|
701
|
-
};
|
|
702
|
-
// Add user message to UI immediately
|
|
703
|
-
setMessages(prev => [...prev, userMessage]);
|
|
704
|
-
const currentInput = message.trim();
|
|
705
|
-
setIsLoading(true);
|
|
706
|
-
// Call the callback if provided
|
|
707
|
-
if (onUserMessage) {
|
|
708
|
-
onUserMessage(userMessage);
|
|
709
|
-
}
|
|
710
|
-
try {
|
|
711
|
-
// Call Blumessage API
|
|
712
|
-
const requestBody = {
|
|
713
|
-
message: currentInput,
|
|
714
|
-
};
|
|
715
|
-
// Include token if we have one
|
|
716
|
-
if (token) {
|
|
717
|
-
requestBody.token = token;
|
|
718
|
-
}
|
|
719
|
-
// Include user context if provided
|
|
720
|
-
if (context && Object.keys(context).length > 0) {
|
|
721
|
-
requestBody.context = context;
|
|
722
|
-
}
|
|
723
|
-
const response = await fetch('https://api.blumessage.com/api/v1/conversations', {
|
|
724
|
-
method: 'POST',
|
|
725
|
-
headers: {
|
|
726
|
-
'Content-Type': 'application/json',
|
|
727
|
-
'Authorization': `Bearer ${apiKey}`,
|
|
728
|
-
},
|
|
729
|
-
body: JSON.stringify(requestBody),
|
|
730
|
-
});
|
|
731
|
-
if (response.ok) {
|
|
732
|
-
const apiResponse = await response.json();
|
|
733
|
-
// Update token from response (for both first message and continuing conversations)
|
|
734
|
-
updateConversationToken(apiResponse.token);
|
|
735
|
-
// Find the assistant's response (the last message that's not from user)
|
|
736
|
-
const assistantMessages = apiResponse.messages.filter(msg => msg.role === 'assistant');
|
|
737
|
-
const latestAssistantMessage = assistantMessages[assistantMessages.length - 1];
|
|
738
|
-
if (latestAssistantMessage) {
|
|
739
|
-
// Add assistant's response to messages
|
|
740
|
-
const assistantResponse = {
|
|
741
|
-
id: (Date.now() + 1).toString(),
|
|
742
|
-
content: latestAssistantMessage.content,
|
|
743
|
-
role: 'assistant',
|
|
744
|
-
timestamp: latestAssistantMessage.timestamp,
|
|
745
|
-
};
|
|
746
|
-
setMessages(prev => [...prev, assistantResponse]);
|
|
747
|
-
if (onAssistantMessage) {
|
|
748
|
-
onAssistantMessage(assistantResponse);
|
|
749
|
-
}
|
|
750
|
-
}
|
|
751
|
-
}
|
|
752
|
-
else {
|
|
753
|
-
// If token is invalid, clear it
|
|
754
|
-
if (response.status === 401 || response.status === 403) {
|
|
755
|
-
updateConversationToken(null);
|
|
756
|
-
}
|
|
757
|
-
// Add error message
|
|
758
|
-
const errorMessage = {
|
|
759
|
-
id: (Date.now() + 1).toString(),
|
|
760
|
-
content: "Sorry, I'm having trouble connecting right now. Please try again.",
|
|
761
|
-
role: 'assistant',
|
|
762
|
-
timestamp: Date.now(),
|
|
763
|
-
};
|
|
764
|
-
setMessages(prev => [...prev, errorMessage]);
|
|
765
|
-
if (onError) {
|
|
766
|
-
onError(`Failed to send message: ${response.status} ${response.statusText}`, "message_send");
|
|
767
|
-
}
|
|
768
|
-
}
|
|
769
|
-
}
|
|
770
|
-
catch (error) {
|
|
771
|
-
// Add error message
|
|
772
|
-
const errorMessage = {
|
|
773
|
-
id: (Date.now() + 1).toString(),
|
|
774
|
-
content: "Sorry, I'm having trouble connecting right now. Please try again.",
|
|
775
|
-
role: 'assistant',
|
|
776
|
-
timestamp: Date.now(),
|
|
777
|
-
};
|
|
778
|
-
setMessages(prev => [...prev, errorMessage]);
|
|
779
|
-
if (onError) {
|
|
780
|
-
onError(`Error sending message: ${error}`, "message_send");
|
|
781
|
-
}
|
|
782
|
-
}
|
|
783
|
-
finally {
|
|
784
|
-
setIsLoading(false);
|
|
785
|
-
}
|
|
786
|
-
},
|
|
787
|
-
openChat: () => {
|
|
788
|
-
if (!isOpen && !isAnimating) {
|
|
789
|
-
setIsAnimating(true);
|
|
790
|
-
// Small delay to ensure smooth animation start
|
|
791
|
-
setTimeout(() => {
|
|
792
|
-
setIsOpen(true);
|
|
793
|
-
// Call the callback after the chat is open
|
|
794
|
-
if (onChatWidgetOpen) {
|
|
795
|
-
onChatWidgetOpen();
|
|
796
|
-
}
|
|
797
|
-
}, 10);
|
|
798
|
-
// Animation duration
|
|
799
|
-
setTimeout(() => {
|
|
800
|
-
setIsAnimating(false);
|
|
801
|
-
}, 300);
|
|
802
|
-
}
|
|
803
|
-
},
|
|
804
|
-
closeChat: () => {
|
|
805
|
-
if (isOpen && !isAnimating) {
|
|
806
|
-
setIsAnimating(true);
|
|
807
|
-
setIsOpen(false);
|
|
808
|
-
// Reset maximized state when closing
|
|
809
|
-
setIsMaximized(false);
|
|
810
|
-
// Call the callback when closing
|
|
811
|
-
if (onChatWidgetClosed) {
|
|
812
|
-
onChatWidgetClosed();
|
|
813
|
-
}
|
|
814
|
-
// Wait for animation to complete
|
|
815
|
-
setTimeout(() => {
|
|
816
|
-
setIsAnimating(false);
|
|
817
|
-
}, 300);
|
|
818
|
-
}
|
|
819
|
-
},
|
|
820
|
-
clearConversation: () => {
|
|
821
|
-
setMessages([]);
|
|
822
|
-
updateConversationToken(null);
|
|
823
|
-
},
|
|
824
|
-
getMessages: () => messages,
|
|
825
|
-
getToken: () => token,
|
|
826
|
-
}));
|
|
827
|
-
// Helper function to get position styles for floating button
|
|
828
|
-
const getButtonPositionStyles = () => {
|
|
829
|
-
const baseStyles = {
|
|
830
|
-
position: 'fixed',
|
|
831
|
-
zIndex: 1000,
|
|
832
|
-
opacity: isOpen ? 0 : 1,
|
|
833
|
-
pointerEvents: isOpen ? 'none' : 'auto',
|
|
834
|
-
transition: 'opacity 0.3s cubic-bezier(0.4, 0, 0.2, 1)',
|
|
835
|
-
};
|
|
836
|
-
// Mobile-specific positioning
|
|
837
|
-
if (isMobile) {
|
|
838
|
-
return {
|
|
839
|
-
...baseStyles,
|
|
840
|
-
bottom: '20px',
|
|
841
|
-
right: '20px',
|
|
842
|
-
left: 'auto',
|
|
843
|
-
top: 'auto'
|
|
844
|
-
};
|
|
845
|
-
}
|
|
846
|
-
// Desktop positioning
|
|
847
|
-
switch (buttonPosition) {
|
|
848
|
-
case 'bottom-right':
|
|
849
|
-
return { ...baseStyles, bottom: '24px', right: '24px' };
|
|
850
|
-
case 'bottom-left':
|
|
851
|
-
return { ...baseStyles, bottom: '24px', left: '24px' };
|
|
852
|
-
case 'top-right':
|
|
853
|
-
return { ...baseStyles, top: '24px', right: '24px' };
|
|
854
|
-
case 'top-left':
|
|
855
|
-
return { ...baseStyles, top: '24px', left: '24px' };
|
|
856
|
-
default:
|
|
857
|
-
return { ...baseStyles, bottom: '24px', right: '24px' };
|
|
858
|
-
}
|
|
859
|
-
};
|
|
860
|
-
// Helper function to get position styles for help bubble
|
|
861
|
-
const getHelpBubblePositionStyles = () => {
|
|
862
|
-
const baseStyles = {
|
|
863
|
-
position: 'fixed',
|
|
864
|
-
zIndex: 999,
|
|
865
|
-
};
|
|
866
|
-
// Mobile-specific positioning
|
|
867
|
-
if (isMobile) {
|
|
868
|
-
return {
|
|
869
|
-
...baseStyles,
|
|
870
|
-
bottom: '80px',
|
|
871
|
-
right: '20px',
|
|
872
|
-
left: 'auto',
|
|
873
|
-
top: 'auto'
|
|
874
|
-
};
|
|
875
|
-
}
|
|
876
|
-
// Desktop positioning - position above the button
|
|
877
|
-
switch (buttonPosition) {
|
|
878
|
-
case 'bottom-right':
|
|
879
|
-
return { ...baseStyles, bottom: '80px', right: '24px' };
|
|
880
|
-
case 'bottom-left':
|
|
881
|
-
return { ...baseStyles, bottom: '80px', left: '24px' };
|
|
882
|
-
case 'top-right':
|
|
883
|
-
return { ...baseStyles, top: '80px', right: '24px' };
|
|
884
|
-
case 'top-left':
|
|
885
|
-
return { ...baseStyles, top: '80px', left: '24px' };
|
|
886
|
-
default:
|
|
887
|
-
return { ...baseStyles, bottom: '80px', right: '24px' };
|
|
888
|
-
}
|
|
889
|
-
};
|
|
890
|
-
// Helper function to get position styles for floating chat window
|
|
891
|
-
const getChatPositionStyles = () => {
|
|
892
|
-
const getTransform = () => {
|
|
893
|
-
if (!isOpen) {
|
|
894
|
-
// Slide down for bottom positions, slide up for top positions
|
|
895
|
-
return buttonPosition?.includes('bottom') ? 'translateY(100%)' : 'translateY(-100%)';
|
|
896
|
-
}
|
|
897
|
-
return 'translateY(0)';
|
|
898
|
-
};
|
|
899
|
-
const baseStyles = {
|
|
900
|
-
position: 'fixed',
|
|
901
|
-
zIndex: 999,
|
|
902
|
-
transform: getTransform(),
|
|
903
|
-
transition: 'transform 0.3s cubic-bezier(0.4, 0, 0.2, 1), opacity 0.3s cubic-bezier(0.4, 0, 0.2, 1)',
|
|
904
|
-
opacity: isOpen ? 1 : 0,
|
|
905
|
-
pointerEvents: isOpen ? 'auto' : 'none',
|
|
906
|
-
};
|
|
907
|
-
// Mobile-specific positioning (always fullscreen on mobile)
|
|
908
|
-
if (isMobile) {
|
|
909
|
-
return {
|
|
910
|
-
...baseStyles,
|
|
911
|
-
top: '0',
|
|
912
|
-
left: '0',
|
|
913
|
-
right: '0',
|
|
914
|
-
bottom: '0',
|
|
915
|
-
transform: isOpen ? 'translateY(0)' : 'translateY(100%)',
|
|
916
|
-
transformOrigin: 'bottom center',
|
|
917
|
-
};
|
|
918
|
-
}
|
|
919
|
-
// If fullScreen or maximized, override positioning to be centered and full screen
|
|
920
|
-
if (fullScreen || isMaximized) {
|
|
921
|
-
// Set transform origin and direction based on button position for proper animation
|
|
922
|
-
let transformOrigin = 'center';
|
|
923
|
-
let closedTransform = 'translateY(100%)';
|
|
924
|
-
switch (buttonPosition) {
|
|
925
|
-
case 'bottom-right':
|
|
926
|
-
transformOrigin = 'bottom right';
|
|
927
|
-
closedTransform = 'translateY(100%)';
|
|
928
|
-
break;
|
|
929
|
-
case 'bottom-left':
|
|
930
|
-
transformOrigin = 'bottom left';
|
|
931
|
-
closedTransform = 'translateY(100%)';
|
|
932
|
-
break;
|
|
933
|
-
case 'top-right':
|
|
934
|
-
transformOrigin = 'top right';
|
|
935
|
-
closedTransform = 'translateY(-100%)';
|
|
936
|
-
break;
|
|
937
|
-
case 'top-left':
|
|
938
|
-
transformOrigin = 'top left';
|
|
939
|
-
closedTransform = 'translateY(-100%)';
|
|
940
|
-
break;
|
|
941
|
-
default:
|
|
942
|
-
transformOrigin = 'bottom right';
|
|
943
|
-
closedTransform = 'translateY(100%)';
|
|
944
|
-
}
|
|
945
|
-
return {
|
|
946
|
-
...baseStyles,
|
|
947
|
-
top: '20px',
|
|
948
|
-
left: '20px',
|
|
949
|
-
right: '20px',
|
|
950
|
-
bottom: '20px',
|
|
951
|
-
transform: isOpen ? 'translateY(0)' : closedTransform,
|
|
952
|
-
transformOrigin,
|
|
953
|
-
};
|
|
954
|
-
}
|
|
955
|
-
// Desktop positioning
|
|
956
|
-
switch (buttonPosition) {
|
|
957
|
-
case 'bottom-right':
|
|
958
|
-
return { ...baseStyles, bottom: '24px', right: '24px', transformOrigin: 'bottom right' };
|
|
959
|
-
case 'bottom-left':
|
|
960
|
-
return { ...baseStyles, bottom: '24px', left: '24px', transformOrigin: 'bottom left' };
|
|
961
|
-
case 'top-right':
|
|
962
|
-
return { ...baseStyles, top: '24px', right: '24px', transformOrigin: 'top right' };
|
|
963
|
-
case 'top-left':
|
|
964
|
-
return { ...baseStyles, top: '24px', left: '24px', transformOrigin: 'top left' };
|
|
965
|
-
default:
|
|
966
|
-
return { ...baseStyles, bottom: '24px', right: '24px', transformOrigin: 'bottom right' };
|
|
967
|
-
}
|
|
968
|
-
};
|
|
969
|
-
const renderMessage = (message) => {
|
|
970
|
-
return ((0, jsx_runtime_1.jsx)("div", { className: `blumessage-flex blumessage-gap-3 blumessage-mb-4 ${message.role === 'user' ? 'blumessage-flex-row-reverse' : ''}`, children: (0, jsx_runtime_1.jsxs)("div", { className: "blumessage-flex blumessage-flex-col blumessage-gap-1 blumessage-max-w-[80%]", children: [(0, jsx_runtime_1.jsx)("div", { className: `blumessage-rounded-lg blumessage-px-4 blumessage-py-2 ${message.role === 'user'
|
|
971
|
-
? 'blumessage-bg-gradient-to-r blumessage-from-blue-500 blumessage-to-purple-600 blumessage-text-white'
|
|
972
|
-
: theme === 'light'
|
|
973
|
-
? 'blumessage-bg-gray-100'
|
|
974
|
-
: 'blumessage-bg-gray-700'}`, children: message.role === 'assistant' ? ((0, jsx_runtime_1.jsx)("div", { className: `blumessage-prose ${theme === 'dark' ? 'blumessage-text-white' : 'blumessage-text-gray-900'}`, children: markdown ? ((0, jsx_runtime_1.jsx)(react_markdown_1.default, { remarkPlugins: [remark_gfm_1.default], children: message.content }, `assistant-${message.id}`)) : ((0, jsx_runtime_1.jsx)("p", { className: "blumessage-text-sm blumessage-whitespace-pre-wrap", children: message.content })) })) : ((0, jsx_runtime_1.jsx)("div", { className: "blumessage-text-sm blumessage-whitespace-pre-wrap blumessage-text-white", children: markdown ? ((0, jsx_runtime_1.jsx)(react_markdown_1.default, { remarkPlugins: [remark_gfm_1.default], children: message.content }, `user-${message.id}`)) : ((0, jsx_runtime_1.jsx)("p", { className: "blumessage-text-sm blumessage-whitespace-pre-wrap", children: message.content })) })) }), showTimestamps && ((0, jsx_runtime_1.jsx)("span", { className: "blumessage-text-xs blumessage-text-gray-500", children: formatTimestamp(message.timestamp) }))] }) }, message.id));
|
|
975
|
-
};
|
|
976
|
-
// Render chat window component
|
|
977
|
-
const renderChatWindow = () => {
|
|
978
|
-
const isFullscreenMode = fullScreen || isMaximized || isMobile;
|
|
979
|
-
const chatContent = ((0, jsx_runtime_1.jsxs)("div", { className: `blumessage-shadow-2xl blumessage-flex blumessage-flex-col blumessage-overflow-hidden blumessage-border ${isMobile ? 'blumessage-mobile-fullscreen' : ''} ${theme === 'dark'
|
|
980
|
-
? 'blumessage-bg-gray-900 blumessage-border-gray-700'
|
|
981
|
-
: 'blumessage-bg-white blumessage-border-black/10'}`, style: {
|
|
982
|
-
width: isFullscreenMode ? '100%' : actualWidth,
|
|
983
|
-
height: isFullscreenMode ? '100%' : actualHeight,
|
|
984
|
-
fontFamily: 'system-ui, -apple-system, sans-serif',
|
|
985
|
-
borderRadius: isMobile ? '0' : (isFullscreenMode ? '12px' : '24px')
|
|
986
|
-
}, children: [(0, jsx_runtime_1.jsxs)("div", { className: `blumessage-border-b ${theme === 'dark'
|
|
987
|
-
? 'blumessage-bg-gray-900 blumessage-border-gray-700'
|
|
988
|
-
: 'blumessage-bg-white blumessage-border-gray-100'}`, style: {
|
|
989
|
-
display: 'blumessage-flex',
|
|
990
|
-
alignItems: 'center',
|
|
991
|
-
padding: isMobile ? '12px 16px' : '16px 24px'
|
|
992
|
-
}, children: [(0, jsx_runtime_1.jsx)("div", { className: "blumessage-rounded-full blumessage-flex blumessage-items-center blumessage-justify-center", style: {
|
|
993
|
-
backgroundImage: primaryColor,
|
|
994
|
-
width: isMobile ? '40px' : '48px',
|
|
995
|
-
height: isMobile ? '40px' : '48px',
|
|
996
|
-
marginRight: isMobile ? '16px' : '24px'
|
|
997
|
-
}, children: react_1.default.createElement(getIconComponent(), {
|
|
998
|
-
className: isMobile ? "blumessage-w-5 blumessage-h-5 blumessage-text-white" : "blumessage-w-6 blumessage-h-6 blumessage-text-white"
|
|
999
|
-
}) }), (0, jsx_runtime_1.jsxs)("div", { style: { flex: 1 }, children: [(0, jsx_runtime_1.jsx)("div", { className: `${isMobile ? 'blumessage-text-base' : 'blumessage-text-lg'} blumessage-font-semibold blumessage-m-0 blumessage-leading-6 ${theme === 'dark' ? 'blumessage-text-gray-100' : 'blumessage-text-gray-900'}`, children: name }), (0, jsx_runtime_1.jsx)("div", { className: `blumessage-text-sm blumessage-m-0 blumessage-leading-5 ${theme === 'dark' ? 'blumessage-text-gray-400' : 'blumessage-text-gray-500'}`, children: subtitle })] }), floating && ((0, jsx_runtime_1.jsxs)("div", { className: "blumessage-flex blumessage-items-center blumessage-gap-1", children: [maximizeToggleButton && !isMobile && ((0, jsx_runtime_1.jsx)("button", { onClick: handleToggleMaximize, className: `blumessage-w-8 blumessage-h-8 blumessage-rounded-full blumessage-flex blumessage-items-center blumessage-justify-center blumessage-cursor-pointer blumessage-transition-colors ${theme === 'dark'
|
|
1000
|
-
? 'hover:blumessage-bg-gray-800 blumessage-text-gray-400 hover:blumessage-text-gray-200'
|
|
1001
|
-
: 'hover:blumessage-bg-gray-100 blumessage-text-gray-500 hover:blumessage-text-gray-700'}`, children: isMaximized ? (0, jsx_runtime_1.jsx)(LucideIcons.Minimize2, { className: "blumessage-w-4 blumessage-h-4" }) : (0, jsx_runtime_1.jsx)(LucideIcons.Maximize, { className: "blumessage-w-4 blumessage-h-4" }) })), (0, jsx_runtime_1.jsx)("button", { onClick: handleCloseChat, className: `blumessage-w-8 blumessage-h-8 blumessage-rounded-full blumessage-flex blumessage-items-center blumessage-justify-center blumessage-cursor-pointer blumessage-transition-colors ${theme === 'dark'
|
|
1002
|
-
? 'hover:blumessage-bg-gray-800 blumessage-text-gray-400 hover:blumessage-text-gray-200'
|
|
1003
|
-
: 'hover:blumessage-bg-gray-100 blumessage-text-gray-500 hover:blumessage-text-gray-700'}`, children: (0, jsx_runtime_1.jsx)(LucideIcons.X, { className: "blumessage-w-4 blumessage-h-4" }) })] }))] }), (0, jsx_runtime_1.jsxs)("div", { className: `blumessage-flex-1 blumessage-overflow-y-auto ${theme === 'dark' ? 'blumessage-bg-gray-800' : 'blumessage-bg-gray-50'}`, style: { padding: isMobile ? '12px 16px' : '16px 24px' }, children: [messages.map((message) => ((0, jsx_runtime_1.jsx)("div", { className: `blumessage-flex ${message.role === 'user' ? 'blumessage-justify-end' : 'blumessage-justify-start'} blumessage-mb-4`, children: (0, jsx_runtime_1.jsx)("div", { className: `${isMobile ? 'blumessage-max-w-[85%]' : 'blumessage-max-w-[80%]'} blumessage-inline-block ${message.role === 'user' ? '' : 'blumessage-text-left'}`, children: (0, jsx_runtime_1.jsxs)("div", { className: `blumessage-inline-block blumessage-px-4 blumessage-py-3 blumessage-rounded-2xl blumessage-text-sm blumessage-leading-6 blumessage-text-left ${message.role === 'user'
|
|
1004
|
-
? 'blumessage-text-white'
|
|
1005
|
-
: theme === 'dark'
|
|
1006
|
-
? 'blumessage-bg-gray-700 blumessage-text-gray-100 blumessage-border blumessage-border-gray-600 blumessage-shadow-sm'
|
|
1007
|
-
: 'blumessage-bg-white blumessage-text-gray-800 blumessage-border blumessage-border-gray-200 blumessage-shadow-sm'}`, style: message.role === 'user' ? { backgroundImage: primaryColor } : {}, children: [message.role === 'assistant' ? (markdown ? ((0, jsx_runtime_1.jsx)(react_markdown_1.default, { remarkPlugins: [remark_gfm_1.default], children: message.content }, `assistant-${message.id}`)) : ((0, jsx_runtime_1.jsx)("div", { className: "blumessage-whitespace-pre-wrap", children: message.content }))) : ((0, jsx_runtime_1.jsx)("div", { className: "blumessage-inline-block", children: markdown ? ((0, jsx_runtime_1.jsx)(react_markdown_1.default, { remarkPlugins: [remark_gfm_1.default], children: message.content }, `user-${message.id}`)) : ((0, jsx_runtime_1.jsx)("div", { className: "blumessage-whitespace-pre-wrap", children: message.content })) })), showTimestamps && ((0, jsx_runtime_1.jsx)("div", { className: `blumessage-text-[10px] blumessage-mt-1 blumessage-opacity-75 ${message.role === 'user'
|
|
1008
|
-
? 'blumessage-text-white/75'
|
|
1009
|
-
: theme === 'dark'
|
|
1010
|
-
? 'blumessage-text-gray-400'
|
|
1011
|
-
: 'blumessage-text-gray-500'}`, style: {
|
|
1012
|
-
textAlign: message.role === 'user' ? 'right' : 'left'
|
|
1013
|
-
}, children: formatTimestamp(message.timestamp) }))] }) }) }, message.id))), isLoading && ((0, jsx_runtime_1.jsx)("div", { className: "blumessage-flex blumessage-justify-start blumessage-mb-4", children: (0, jsx_runtime_1.jsx)("div", { className: `blumessage-px-4 blumessage-py-3 blumessage-rounded-2xl ${isMobile ? 'blumessage-max-w-[85%]' : 'blumessage-max-w-[80%]'} blumessage-text-sm blumessage-leading-6 blumessage-shadow-sm ${theme === 'dark'
|
|
1014
|
-
? 'blumessage-bg-gray-700 blumessage-text-gray-100 blumessage-border blumessage-border-gray-600'
|
|
1015
|
-
: 'blumessage-bg-white blumessage-text-gray-800 blumessage-border blumessage-border-gray-200'}`, children: (0, jsx_runtime_1.jsx)("div", { className: `${theme === 'dark' ? 'blumessage-text-gray-300' : 'blumessage-text-gray-600'}`, style: { fontStyle: 'italic' }, children: typingText }) }) })), messages.length === 0 && !isLoading && ((0, jsx_runtime_1.jsx)("div", { className: `blumessage-text-center blumessage-text-sm blumessage-py-8 ${theme === 'dark' ? 'blumessage-text-gray-400' : 'blumessage-text-gray-500'}`, children: emptyStateText })), (0, jsx_runtime_1.jsx)("div", { ref: messagesEndRef })] }), (0, jsx_runtime_1.jsx)("div", { className: `blumessage-border-t ${theme === 'dark'
|
|
1016
|
-
? 'blumessage-bg-gray-900 blumessage-border-gray-700'
|
|
1017
|
-
: 'blumessage-bg-white blumessage-border-gray-100'}`, style: { padding: isMobile ? '12px 16px' : '16px 24px' }, children: (0, jsx_runtime_1.jsxs)("div", { className: `blumessage-flex blumessage-items-center blumessage-rounded-2xl blumessage-px-4 blumessage-py-3 blumessage-border ${theme === 'dark'
|
|
1018
|
-
? 'blumessage-bg-gray-800 blumessage-border-gray-600'
|
|
1019
|
-
: 'blumessage-bg-gray-50 blumessage-border-gray-200'}`, children: [(0, jsx_runtime_1.jsx)("textarea", { className: `blumessage-flex-1 blumessage-border-none blumessage-bg-transparent blumessage-outline-none blumessage-text-sm blumessage-font-inherit blumessage-resize-none ${theme === 'dark' ? 'blumessage-text-gray-100' : 'blumessage-text-gray-700'}`, placeholder: placeholder, value: inputValue, onChange: (e) => {
|
|
1020
|
-
const newValue = e.target.value;
|
|
1021
|
-
if (newValue.length <= 1000) {
|
|
1022
|
-
setInputValue(newValue);
|
|
1023
|
-
}
|
|
1024
|
-
}, onPaste: (e) => {
|
|
1025
|
-
e.preventDefault();
|
|
1026
|
-
const pastedText = e.clipboardData.getData('text');
|
|
1027
|
-
const currentValue = inputValue;
|
|
1028
|
-
const newValue = currentValue + pastedText;
|
|
1029
|
-
if (newValue.length <= 1000) {
|
|
1030
|
-
setInputValue(newValue);
|
|
1031
|
-
}
|
|
1032
|
-
else {
|
|
1033
|
-
// Truncate to exactly 1000 characters
|
|
1034
|
-
const truncatedValue = newValue.substring(0, 1000);
|
|
1035
|
-
setInputValue(truncatedValue);
|
|
1036
|
-
}
|
|
1037
|
-
}, onKeyDown: handleKeyPress, rows: 1, maxLength: 1000, style: {
|
|
1038
|
-
minHeight: '20px',
|
|
1039
|
-
maxHeight: isMobile ? '100px' : '120px',
|
|
1040
|
-
overflowY: inputValue.split('\n').length > 4 ? 'auto' : 'hidden',
|
|
1041
|
-
lineHeight: '1.5',
|
|
1042
|
-
paddingTop: '2px',
|
|
1043
|
-
paddingBottom: '2px'
|
|
1044
|
-
}, onInput: (e) => {
|
|
1045
|
-
const target = e.target;
|
|
1046
|
-
target.style.height = 'auto';
|
|
1047
|
-
target.style.height = Math.min(target.scrollHeight, isMobile ? 100 : 120) + 'px';
|
|
1048
|
-
} }), (0, jsx_runtime_1.jsx)("button", { className: "blumessage-w-8 blumessage-h-8 blumessage-rounded-full blumessage-border-none blumessage-flex blumessage-items-center blumessage-justify-center blumessage-cursor-pointer blumessage-ml-2 blumessage-text-white blumessage-transition-all disabled:blumessage-opacity-50 disabled:blumessage-cursor-not-allowed", style: { backgroundImage: primaryColor }, onClick: handleSendMessage, disabled: !inputValue.trim() || isLoading, children: isLoading ? ((0, jsx_runtime_1.jsx)(LucideIcons.Loader2, { className: "blumessage-w-4 blumessage-h-4 blumessage-animate-spin" })) : ((0, jsx_runtime_1.jsx)(LucideIcons.Send, { className: "blumessage-w-4 blumessage-h-4" })) })] }) })] }));
|
|
1049
|
-
if (floating) {
|
|
1050
|
-
return ((0, jsx_runtime_1.jsx)("div", { style: getChatPositionStyles(), children: chatContent }));
|
|
1051
|
-
}
|
|
1052
|
-
return chatContent;
|
|
1053
|
-
};
|
|
1054
|
-
// Don't render anything if no API key is provided
|
|
1055
|
-
if (!apiKey) {
|
|
1056
|
-
return null;
|
|
1057
|
-
}
|
|
1058
|
-
if (error) {
|
|
1059
|
-
const isFullscreenMode = fullScreen || isMaximized || isMobile;
|
|
1060
|
-
const errorContent = ((0, jsx_runtime_1.jsxs)("div", { className: `blumessage-shadow-2xl blumessage-flex blumessage-flex-col blumessage-overflow-hidden blumessage-border ${isMobile ? 'blumessage-mobile-fullscreen' : ''} ${theme === 'dark'
|
|
1061
|
-
? 'blumessage-bg-gray-900 blumessage-border-gray-700'
|
|
1062
|
-
: 'blumessage-bg-white blumessage-border-black/10'}`, style: {
|
|
1063
|
-
width: isFullscreenMode ? '100%' : actualWidth,
|
|
1064
|
-
height: isFullscreenMode ? '100%' : actualHeight,
|
|
1065
|
-
fontFamily: 'system-ui, -apple-system, sans-serif',
|
|
1066
|
-
borderRadius: isMobile ? '0' : (isFullscreenMode ? '12px' : '24px')
|
|
1067
|
-
}, children: [(0, jsx_runtime_1.jsxs)("div", { className: `blumessage-flex blumessage-items-center blumessage-border-b ${theme === 'dark'
|
|
1068
|
-
? 'blumessage-bg-gray-900 blumessage-border-gray-700'
|
|
1069
|
-
: 'blumessage-bg-white blumessage-border-gray-100'}`, style: { padding: isMobile ? '12px 16px' : '16px 24px' }, children: [(0, jsx_runtime_1.jsx)("div", { className: "blumessage-rounded-full blumessage-flex blumessage-items-center blumessage-justify-center", style: {
|
|
1070
|
-
backgroundColor: '#ef4444',
|
|
1071
|
-
width: isMobile ? '40px' : '48px',
|
|
1072
|
-
height: isMobile ? '40px' : '48px',
|
|
1073
|
-
marginRight: isMobile ? '16px' : '24px'
|
|
1074
|
-
}, children: (0, jsx_runtime_1.jsx)(LucideIcons.AlertTriangle, { className: isMobile ? "blumessage-w-5 blumessage-h-5 blumessage-text-white" : "blumessage-w-6 blumessage-h-6 blumessage-text-white" }) }), (0, jsx_runtime_1.jsxs)("div", { className: "blumessage-flex-1", children: [(0, jsx_runtime_1.jsx)("div", { className: `${isMobile ? 'blumessage-text-base' : 'blumessage-text-lg'} blumessage-font-semibold blumessage-m-0 blumessage-leading-6 ${theme === 'dark' ? 'blumessage-text-gray-100' : 'blumessage-text-gray-900'}`, children: "Connection Error" }), (0, jsx_runtime_1.jsx)("div", { className: `blumessage-text-sm blumessage-m-0 blumessage-leading-5 ${theme === 'dark' ? 'blumessage-text-gray-400' : 'blumessage-text-gray-500'}`, children: "Unable to connect" })] }), floating && ((0, jsx_runtime_1.jsxs)("div", { className: "blumessage-flex blumessage-items-center blumessage-gap-1", children: [maximizeToggleButton && !isMobile && ((0, jsx_runtime_1.jsx)("button", { onClick: handleToggleMaximize, className: `blumessage-w-8 blumessage-h-8 blumessage-rounded-full blumessage-flex blumessage-items-center blumessage-justify-center blumessage-cursor-pointer blumessage-transition-colors ${theme === 'dark'
|
|
1075
|
-
? 'hover:blumessage-bg-gray-800 blumessage-text-gray-400 hover:blumessage-text-gray-200'
|
|
1076
|
-
: 'hover:blumessage-bg-gray-100 blumessage-text-gray-500 hover:blumessage-text-gray-700'}`, children: isMaximized ? (0, jsx_runtime_1.jsx)(LucideIcons.Minimize2, { className: "blumessage-w-4 blumessage-h-4" }) : (0, jsx_runtime_1.jsx)(LucideIcons.Maximize, { className: "blumessage-w-4 blumessage-h-4" }) })), (0, jsx_runtime_1.jsx)("button", { onClick: handleCloseChat, className: `blumessage-w-8 blumessage-h-8 blumessage-rounded-full blumessage-flex blumessage-items-center blumessage-justify-center blumessage-cursor-pointer blumessage-transition-colors ${theme === 'dark'
|
|
1077
|
-
? 'hover:blumessage-bg-gray-800 blumessage-text-gray-400 hover:blumessage-text-gray-200'
|
|
1078
|
-
: 'hover:blumessage-bg-gray-100 blumessage-text-gray-500 hover:blumessage-text-gray-700'}`, children: (0, jsx_runtime_1.jsx)(LucideIcons.X, { className: "blumessage-w-4 blumessage-h-4" }) })] }))] }), (0, jsx_runtime_1.jsx)("div", { className: `blumessage-flex-1 blumessage-overflow-y-auto ${theme === 'dark' ? 'blumessage-bg-gray-800' : 'blumessage-bg-gray-50'}`, style: { padding: isMobile ? '12px 16px' : '16px 24px' }, children: (0, jsx_runtime_1.jsx)("div", { className: "blumessage-flex blumessage-justify-start", children: (0, jsx_runtime_1.jsx)("div", { className: `blumessage-px-4 blumessage-py-3 blumessage-rounded-2xl ${isMobile ? 'blumessage-max-w-[85%]' : 'blumessage-max-w-[80%]'} blumessage-text-sm blumessage-leading-6 blumessage-shadow-sm ${theme === 'dark'
|
|
1079
|
-
? 'blumessage-bg-gray-700 blumessage-text-gray-100 blumessage-border blumessage-border-gray-600'
|
|
1080
|
-
: 'blumessage-bg-white blumessage-text-gray-800 blumessage-border blumessage-border-gray-200'}`, children: error }) }) })] }));
|
|
1081
|
-
if (floating) {
|
|
1082
|
-
return ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsxs)("button", { onClick: handleOpenChat, className: `blumessage-text-white blumessage-rounded-full blumessage-shadow-lg blumessage-transition-all blumessage-duration-200 blumessage-flex blumessage-items-center blumessage-justify-center blumessage-gap-2 ${isMobile ? 'blumessage-mobile-button' : ''} ${buttonText ? 'blumessage-px-4 blumessage-py-3 blumessage-h-12' : 'blumessage-w-14 blumessage-h-14'}`, style: { ...getButtonPositionStyles(), ...buttonStyle, backgroundImage: primaryColor }, children: [react_1.default.createElement(getIconComponent(), { className: "blumessage-w-6 blumessage-h-6" }), buttonText && !isMobile && (0, jsx_runtime_1.jsx)("span", { className: "blumessage-text-sm blumessage-font-medium blumessage-whitespace-nowrap", children: buttonText })] }), showHelpBubble && (isHelpBubbleVisible || isHelpBubbleAnimating) && ((0, jsx_runtime_1.jsx)("div", { className: `blumessage-fixed blumessage-z-[999] blumessage-border blumessage-rounded-lg blumessage-shadow-lg blumessage-px-3 blumessage-py-2 blumessage-text-sm blumessage-max-w-[200px] ${isHelpBubbleVisible
|
|
1083
|
-
? (buttonPosition?.includes('top') ? 'blumessage-help-bubble-in-top' : 'blumessage-help-bubble-in')
|
|
1084
|
-
: (buttonPosition?.includes('top') ? 'blumessage-help-bubble-out-top' : 'blumessage-help-bubble-out')} ${theme === 'dark'
|
|
1085
|
-
? 'blumessage-bg-gray-800 blumessage-border-gray-600'
|
|
1086
|
-
: 'blumessage-bg-white blumessage-border-gray-200'}`, style: {
|
|
1087
|
-
...getHelpBubblePositionStyles(),
|
|
1088
|
-
backgroundColor: helpBubbleBackgroundColor || (theme === 'dark' ? '#1f2937' : '#ffffff'),
|
|
1089
|
-
transformOrigin: buttonPosition?.includes('bottom') ? 'bottom center' : 'top center',
|
|
1090
|
-
}, children: (0, jsx_runtime_1.jsxs)("div", { className: "blumessage-flex blumessage-items-center blumessage-gap-2", children: [react_1.default.createElement(getHelpBubbleIconComponent(), {
|
|
1091
|
-
className: "blumessage-w-4 blumessage-h-4 blumessage-flex-shrink-0",
|
|
1092
|
-
style: { color: primaryColor.includes('linear-gradient') ? '#3b82f6' : primaryColor }
|
|
1093
|
-
}), (0, jsx_runtime_1.jsx)("span", { className: `blumessage-leading-tight ${getTextColor(helpBubbleBackgroundColor || (theme === 'dark' ? '#1f2937' : '#ffffff'), theme)}`, children: helpBubbleMessage })] }) })), (0, jsx_runtime_1.jsx)("div", { style: getChatPositionStyles(), children: errorContent })] }));
|
|
1094
|
-
}
|
|
1095
|
-
return errorContent;
|
|
1096
|
-
}
|
|
1097
|
-
if (!isInitialized) {
|
|
1098
|
-
const isFullscreenMode = fullScreen || isMaximized || isMobile;
|
|
1099
|
-
const loadingContent = ((0, jsx_runtime_1.jsxs)("div", { className: `blumessage-shadow-2xl blumessage-flex blumessage-flex-col blumessage-overflow-hidden blumessage-border ${isMobile ? 'blumessage-mobile-fullscreen' : ''} ${theme === 'dark'
|
|
1100
|
-
? 'blumessage-bg-gray-900 blumessage-border-gray-700'
|
|
1101
|
-
: 'blumessage-bg-white blumessage-border-black/10'}`, style: {
|
|
1102
|
-
width: isFullscreenMode ? '100%' : actualWidth,
|
|
1103
|
-
height: isFullscreenMode ? '100%' : actualHeight,
|
|
1104
|
-
fontFamily: 'system-ui, -apple-system, sans-serif',
|
|
1105
|
-
borderRadius: isMobile ? '0' : (isFullscreenMode ? '12px' : '24px')
|
|
1106
|
-
}, children: [(0, jsx_runtime_1.jsxs)("div", { className: `blumessage-flex blumessage-items-center blumessage-border-b ${theme === 'dark'
|
|
1107
|
-
? 'blumessage-bg-gray-900 blumessage-border-gray-700'
|
|
1108
|
-
: 'blumessage-bg-white blumessage-border-gray-100'}`, style: { padding: isMobile ? '12px 16px' : '16px 24px' }, children: [(0, jsx_runtime_1.jsx)("div", { className: "blumessage-rounded-full blumessage-flex blumessage-items-center blumessage-justify-center", style: {
|
|
1109
|
-
backgroundImage: primaryColor,
|
|
1110
|
-
width: isMobile ? '40px' : '48px',
|
|
1111
|
-
height: isMobile ? '40px' : '48px',
|
|
1112
|
-
marginRight: isMobile ? '16px' : '24px'
|
|
1113
|
-
}, children: (0, jsx_runtime_1.jsx)(LucideIcons.Loader2, { className: isMobile ? "blumessage-w-5 blumessage-h-5 blumessage-text-white blumessage-animate-spin" : "blumessage-w-6 blumessage-h-6 blumessage-text-white blumessage-animate-spin" }) }), (0, jsx_runtime_1.jsxs)("div", { className: "blumessage-flex-1", children: [(0, jsx_runtime_1.jsx)("div", { className: `${isMobile ? 'blumessage-text-base' : 'blumessage-text-lg'} blumessage-font-semibold blumessage-m-0 blumessage-leading-6 ${theme === 'dark' ? 'blumessage-text-gray-100' : 'blumessage-text-gray-900'}`, children: name }), (0, jsx_runtime_1.jsx)("div", { className: `blumessage-text-sm blumessage-m-0 blumessage-leading-5 ${theme === 'dark' ? 'blumessage-text-gray-400' : 'blumessage-text-gray-500'}`, children: subtitle })] }), floating && ((0, jsx_runtime_1.jsxs)("div", { className: "blumessage-flex blumessage-items-center blumessage-gap-1", children: [maximizeToggleButton && !isMobile && ((0, jsx_runtime_1.jsx)("button", { onClick: handleToggleMaximize, className: `blumessage-w-8 blumessage-h-8 blumessage-rounded-full blumessage-flex blumessage-items-center blumessage-justify-center blumessage-cursor-pointer blumessage-transition-colors ${theme === 'dark'
|
|
1114
|
-
? 'hover:blumessage-bg-gray-800 blumessage-text-gray-400 hover:blumessage-text-gray-200'
|
|
1115
|
-
: 'hover:blumessage-bg-gray-100 blumessage-text-gray-500 hover:blumessage-text-gray-700'}`, children: isMaximized ? (0, jsx_runtime_1.jsx)(LucideIcons.Minimize2, { className: "blumessage-w-4 blumessage-h-4" }) : (0, jsx_runtime_1.jsx)(LucideIcons.Maximize, { className: "blumessage-w-4 blumessage-h-4" }) })), (0, jsx_runtime_1.jsx)("button", { onClick: handleCloseChat, className: `blumessage-w-8 blumessage-h-8 blumessage-rounded-full blumessage-flex blumessage-items-center blumessage-justify-center blumessage-cursor-pointer blumessage-transition-colors ${theme === 'dark'
|
|
1116
|
-
? 'hover:blumessage-bg-gray-800 blumessage-text-gray-400 hover:blumessage-text-gray-200'
|
|
1117
|
-
: 'hover:blumessage-bg-gray-100 blumessage-text-gray-500 hover:blumessage-text-gray-700'}`, children: (0, jsx_runtime_1.jsx)(LucideIcons.X, { className: "blumessage-w-4 blumessage-h-4" }) })] }))] }), (0, jsx_runtime_1.jsx)("div", { className: `blumessage-flex-1 blumessage-overflow-y-auto ${theme === 'dark' ? 'blumessage-bg-gray-800' : 'blumessage-bg-gray-50'}`, style: { padding: isMobile ? '12px 16px' : '16px 24px' }, children: (0, jsx_runtime_1.jsx)("div", { className: `blumessage-text-center blumessage-text-sm blumessage-py-8 ${theme === 'dark' ? 'blumessage-text-gray-400' : 'blumessage-text-gray-500'}`, children: "Ready to chat..." }) })] }));
|
|
1118
|
-
if (floating) {
|
|
1119
|
-
return ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsxs)("button", { onClick: handleOpenChat, className: `blumessage-text-white blumessage-rounded-full blumessage-shadow-lg blumessage-transition-all blumessage-duration-200 blumessage-flex blumessage-items-center blumessage-justify-center blumessage-gap-2 ${isMobile ? 'blumessage-mobile-button' : ''} ${buttonText ? 'blumessage-px-4 blumessage-py-3 blumessage-h-12' : 'blumessage-w-14 blumessage-h-14'}`, style: { ...getButtonPositionStyles(), ...buttonStyle, backgroundImage: primaryColor }, children: [react_1.default.createElement(getIconComponent(), { className: "blumessage-w-6 blumessage-h-6" }), buttonText && !isMobile && (0, jsx_runtime_1.jsx)("span", { className: "blumessage-text-sm blumessage-font-medium blumessage-whitespace-nowrap", children: buttonText })] }), showHelpBubble && (isHelpBubbleVisible || isHelpBubbleAnimating) && ((0, jsx_runtime_1.jsx)("div", { className: `blumessage-fixed blumessage-z-[999] blumessage-border blumessage-rounded-lg blumessage-shadow-lg blumessage-px-3 blumessage-py-2 blumessage-text-sm blumessage-max-w-[200px] ${isHelpBubbleVisible
|
|
1120
|
-
? (buttonPosition?.includes('top') ? 'blumessage-help-bubble-in-top' : 'blumessage-help-bubble-in')
|
|
1121
|
-
: (buttonPosition?.includes('top') ? 'blumessage-help-bubble-out-top' : 'blumessage-help-bubble-out')} ${theme === 'dark'
|
|
1122
|
-
? 'blumessage-bg-gray-800 blumessage-border-gray-600'
|
|
1123
|
-
: 'blumessage-bg-white blumessage-border-gray-200'}`, style: {
|
|
1124
|
-
...getHelpBubblePositionStyles(),
|
|
1125
|
-
backgroundColor: helpBubbleBackgroundColor || (theme === 'dark' ? '#1f2937' : '#ffffff'),
|
|
1126
|
-
transformOrigin: buttonPosition?.includes('bottom') ? 'bottom center' : 'top center',
|
|
1127
|
-
}, children: (0, jsx_runtime_1.jsxs)("div", { className: "blumessage-flex blumessage-items-center blumessage-gap-2", children: [react_1.default.createElement(getHelpBubbleIconComponent(), {
|
|
1128
|
-
className: "blumessage-w-4 blumessage-h-4 blumessage-flex-shrink-0",
|
|
1129
|
-
style: { color: primaryColor.includes('linear-gradient') ? '#3b82f6' : primaryColor }
|
|
1130
|
-
}), (0, jsx_runtime_1.jsx)("span", { className: `blumessage-leading-tight ${getTextColor(helpBubbleBackgroundColor || (theme === 'dark' ? '#1f2937' : '#ffffff'), theme)}`, children: helpBubbleMessage })] }) })), (0, jsx_runtime_1.jsx)("div", { style: getChatPositionStyles(), children: loadingContent })] }));
|
|
1131
|
-
}
|
|
1132
|
-
return loadingContent;
|
|
1133
|
-
}
|
|
1134
|
-
// Main render for initialized state
|
|
1135
|
-
if (floating) {
|
|
1136
|
-
return ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsxs)("button", { onClick: handleOpenChat, className: `blumessage-text-white blumessage-rounded-full blumessage-shadow-lg blumessage-transition-all blumessage-duration-200 blumessage-flex blumessage-items-center blumessage-justify-center blumessage-gap-2 ${isMobile ? 'blumessage-mobile-button' : ''} ${buttonText ? 'blumessage-px-4 blumessage-py-3 blumessage-h-12' : 'blumessage-w-14 blumessage-h-14'}`, style: { ...getButtonPositionStyles(), ...buttonStyle, backgroundImage: primaryColor }, children: [react_1.default.createElement(getIconComponent(), { className: "blumessage-w-6 blumessage-h-6" }), buttonText && !isMobile && (0, jsx_runtime_1.jsx)("span", { className: "blumessage-text-sm blumessage-font-medium blumessage-whitespace-nowrap", children: buttonText })] }), showHelpBubble && (isHelpBubbleVisible || isHelpBubbleAnimating) && ((0, jsx_runtime_1.jsx)("div", { className: `blumessage-fixed blumessage-z-[999] blumessage-border blumessage-rounded-lg blumessage-shadow-lg blumessage-px-3 blumessage-py-2 blumessage-text-sm blumessage-max-w-[200px] ${isHelpBubbleVisible
|
|
1137
|
-
? (buttonPosition?.includes('top') ? 'blumessage-help-bubble-in-top' : 'blumessage-help-bubble-in')
|
|
1138
|
-
: (buttonPosition?.includes('top') ? 'blumessage-help-bubble-out-top' : 'blumessage-help-bubble-out')} ${theme === 'dark'
|
|
1139
|
-
? 'blumessage-bg-gray-800 blumessage-border-gray-600'
|
|
1140
|
-
: 'blumessage-bg-white blumessage-border-gray-200'}`, style: {
|
|
1141
|
-
...getHelpBubblePositionStyles(),
|
|
1142
|
-
backgroundColor: helpBubbleBackgroundColor || (theme === 'dark' ? '#1f2937' : '#ffffff'),
|
|
1143
|
-
transformOrigin: buttonPosition?.includes('bottom') ? 'bottom center' : 'top center',
|
|
1144
|
-
}, children: (0, jsx_runtime_1.jsxs)("div", { className: "blumessage-flex blumessage-items-center blumessage-gap-2", children: [react_1.default.createElement(getHelpBubbleIconComponent(), {
|
|
1145
|
-
className: "blumessage-w-4 blumessage-h-4 blumessage-flex-shrink-0",
|
|
1146
|
-
style: { color: primaryColor.includes('linear-gradient') ? '#3b82f6' : primaryColor }
|
|
1147
|
-
}), (0, jsx_runtime_1.jsx)("span", { className: `blumessage-leading-tight ${getTextColor(helpBubbleBackgroundColor || (theme === 'dark' ? '#1f2937' : '#ffffff'), theme)}`, children: helpBubbleMessage })] }) })), renderChatWindow()] }));
|
|
1148
|
-
}
|
|
1149
|
-
return renderChatWindow();
|
|
1150
|
-
});
|
package/dist/index.browser.js
DELETED
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.BlumessageChat = void 0;
|
|
4
|
-
const BlumessageChat_1 = require("./BlumessageChat");
|
|
5
|
-
Object.defineProperty(exports, "BlumessageChat", { enumerable: true, get: function () { return BlumessageChat_1.BlumessageChat; } });
|
|
6
|
-
// Export as default for UMD compatibility
|
|
7
|
-
exports.default = BlumessageChat_1.BlumessageChat;
|
package/dist/index.commonjs.js
DELETED
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
-
};
|
|
5
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.ReactDOM = exports.React = exports.BlumessageChat = void 0;
|
|
7
|
-
const BlumessageChat_1 = require("./BlumessageChat");
|
|
8
|
-
Object.defineProperty(exports, "BlumessageChat", { enumerable: true, get: function () { return BlumessageChat_1.BlumessageChat; } });
|
|
9
|
-
// Export as default for UMD compatibility
|
|
10
|
-
exports.default = BlumessageChat_1.BlumessageChat;
|
|
11
|
-
// Export React and ReactDOM for convenience
|
|
12
|
-
var react_1 = require("react");
|
|
13
|
-
Object.defineProperty(exports, "React", { enumerable: true, get: function () { return __importDefault(react_1).default; } });
|
|
14
|
-
var react_dom_1 = require("react-dom");
|
|
15
|
-
Object.defineProperty(exports, "ReactDOM", { enumerable: true, get: function () { return __importDefault(react_dom_1).default; } });
|