@aslaluroba/help-center-react 3.2.1 → 3.2.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/dist/components/ui/image-attachment.d.ts +2 -1
- package/dist/index.css +1 -1
- package/dist/index.esm.js +295 -125
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +295 -125
- package/dist/index.js.map +1 -1
- package/dist/lib/types.d.ts +1 -0
- package/dist/services.esm.js +71 -14
- package/dist/services.esm.js.map +1 -1
- package/dist/services.js +71 -14
- package/dist/services.js.map +1 -1
- package/dist/ui/chatbot-popup/chat-window-screen/footer.d.ts +1 -0
- package/dist/ui/chatbot-popup/chat-window-screen/index.d.ts +2 -1
- package/package.json +1 -1
- package/src/components/ui/agent-response/agent-response.tsx +5 -3
- package/src/components/ui/image-attachment.tsx +29 -17
- package/src/components/ui/image-preview-dialog.tsx +46 -0
- package/src/core/AblyService.ts +59 -12
- package/src/lib/custom-hooks/useTypewriter.ts +5 -3
- package/src/lib/types.ts +2 -1
- package/src/ui/chatbot-popup/chat-window-screen/footer.tsx +71 -44
- package/src/ui/chatbot-popup/chat-window-screen/index.tsx +69 -18
- package/src/ui/help-center.tsx +13 -8
- package/src/ui/help-popup.tsx +3 -1
package/dist/index.js
CHANGED
|
@@ -16271,10 +16271,19 @@ class ClientAblyService {
|
|
|
16271
16271
|
token: ablyToken,
|
|
16272
16272
|
autoConnect: true
|
|
16273
16273
|
});
|
|
16274
|
+
_this.client.connection.on('failed', stateChange => {
|
|
16275
|
+
var _a;
|
|
16276
|
+
console.error('[AblyService] Connection state: failed', {
|
|
16277
|
+
reason: (_a = stateChange.reason) === null || _a === void 0 ? void 0 : _a.message,
|
|
16278
|
+
error: stateChange.reason
|
|
16279
|
+
});
|
|
16280
|
+
});
|
|
16274
16281
|
// Wait for connection to be established
|
|
16275
16282
|
yield new Promise((resolve, reject) => {
|
|
16276
16283
|
if (!_this.client) {
|
|
16277
|
-
|
|
16284
|
+
var error = new Error('Failed to initialize Ably client');
|
|
16285
|
+
console.error('[AblyService]', error);
|
|
16286
|
+
reject(error);
|
|
16278
16287
|
return;
|
|
16279
16288
|
}
|
|
16280
16289
|
_this.client.connection.once('connected', () => {
|
|
@@ -16283,23 +16292,38 @@ class ClientAblyService {
|
|
|
16283
16292
|
resolve();
|
|
16284
16293
|
});
|
|
16285
16294
|
_this.client.connection.once('failed', stateChange => {
|
|
16286
|
-
var _a;
|
|
16287
|
-
|
|
16295
|
+
var _a, _b;
|
|
16296
|
+
var error = new Error("Ably connection failed: ".concat(((_a = stateChange.reason) === null || _a === void 0 ? void 0 : _a.message) || 'Unknown error'));
|
|
16297
|
+
console.error('[AblyService] Connection failed', {
|
|
16298
|
+
reason: (_b = stateChange.reason) === null || _b === void 0 ? void 0 : _b.message,
|
|
16299
|
+
error: stateChange.reason
|
|
16300
|
+
});
|
|
16301
|
+
reject(error);
|
|
16288
16302
|
});
|
|
16289
16303
|
_this.client.connection.once('disconnected', stateChange => {
|
|
16290
|
-
var _a;
|
|
16291
|
-
|
|
16304
|
+
var _a, _b;
|
|
16305
|
+
var error = new Error("Ably connection disconnected: ".concat(((_a = stateChange.reason) === null || _a === void 0 ? void 0 : _a.message) || 'Unknown error'));
|
|
16306
|
+
console.error('[AblyService] Connection disconnected', {
|
|
16307
|
+
reason: (_b = stateChange.reason) === null || _b === void 0 ? void 0 : _b.message
|
|
16308
|
+
});
|
|
16309
|
+
reject(error);
|
|
16292
16310
|
});
|
|
16293
16311
|
// Set a timeout for connection
|
|
16294
16312
|
setTimeout(() => {
|
|
16295
16313
|
if (!_this.isConnected) {
|
|
16296
|
-
|
|
16314
|
+
var _error = new Error('Ably connection timeout');
|
|
16315
|
+
console.error('[AblyService] Connection timeout after 10 seconds');
|
|
16316
|
+
reject(_error);
|
|
16297
16317
|
}
|
|
16298
16318
|
}, 10000);
|
|
16299
16319
|
});
|
|
16300
16320
|
// Subscribe to the session room
|
|
16301
16321
|
yield _this.joinChannel(sessionId, onMessageReceived, tenantId);
|
|
16302
16322
|
} catch (error) {
|
|
16323
|
+
console.error('[AblyService] Error in startConnection', {
|
|
16324
|
+
error,
|
|
16325
|
+
sessionId
|
|
16326
|
+
});
|
|
16303
16327
|
_this.isConnected = false;
|
|
16304
16328
|
_this.sessionId = null;
|
|
16305
16329
|
throw error;
|
|
@@ -16310,23 +16334,53 @@ class ClientAblyService {
|
|
|
16310
16334
|
var _this2 = this;
|
|
16311
16335
|
return _asyncToGenerator(function* () {
|
|
16312
16336
|
if (!_this2.client) {
|
|
16313
|
-
|
|
16337
|
+
var error = new Error('Chat client not initialized');
|
|
16338
|
+
console.error('[AblyService] joinChannel error:', error);
|
|
16339
|
+
throw error;
|
|
16314
16340
|
}
|
|
16315
16341
|
var roomName = "session:".concat(tenantId, ":").concat(sessionId);
|
|
16316
16342
|
// Set up raw channel subscription for server messages
|
|
16317
16343
|
if (_this2.client) {
|
|
16318
16344
|
_this2.channel = _this2.client.channels.get(roomName);
|
|
16345
|
+
_this2.channel.on('failed', stateChange => {
|
|
16346
|
+
var _a;
|
|
16347
|
+
console.error('[AblyService] Channel failed', {
|
|
16348
|
+
roomName,
|
|
16349
|
+
reason: (_a = stateChange.reason) === null || _a === void 0 ? void 0 : _a.message,
|
|
16350
|
+
error: stateChange.reason
|
|
16351
|
+
});
|
|
16352
|
+
});
|
|
16319
16353
|
// Subscribe to assistant/system responses
|
|
16320
16354
|
_this2.channel.subscribe('ReceiveMessage', message => {
|
|
16321
|
-
var _a, _b, _c, _d, _e, _f;
|
|
16355
|
+
var _a, _b, _c, _d, _e, _f, _g, _h;
|
|
16322
16356
|
try {
|
|
16323
|
-
|
|
16324
|
-
var
|
|
16325
|
-
var
|
|
16326
|
-
var
|
|
16327
|
-
|
|
16357
|
+
// Ensure messageContent is always a string (default to empty string if undefined)
|
|
16358
|
+
var messageContent = typeof message.data === 'string' ? message.data : (_d = (_b = (_a = message.data) === null || _a === void 0 ? void 0 : _a.content) !== null && _b !== void 0 ? _b : (_c = message.data) === null || _c === void 0 ? void 0 : _c.message) !== null && _d !== void 0 ? _d : '';
|
|
16359
|
+
var senderType = ((_e = message.data) === null || _e === void 0 ? void 0 : _e.senderType) || 3; // Assistant
|
|
16360
|
+
var needsAgent = ((_f = message.data) === null || _f === void 0 ? void 0 : _f.needsAgent) || ((_g = message.data) === null || _g === void 0 ? void 0 : _g.actionType) == 'needs_agent' || false;
|
|
16361
|
+
var attachments = ((_h = message.data) === null || _h === void 0 ? void 0 : _h.attachments) || [];
|
|
16362
|
+
// Extract downloadUrl from attachments (Ably now returns downloadUrl directly)
|
|
16363
|
+
// Attachments can be: strings (URLs), objects with downloadUrl, or objects with id
|
|
16364
|
+
var attachmentUrls = attachments.map(attachment => {
|
|
16365
|
+
if (typeof attachment === 'string') {
|
|
16366
|
+
// If it's already a string, it's a URL
|
|
16367
|
+
return attachment;
|
|
16368
|
+
} else if (attachment === null || attachment === void 0 ? void 0 : attachment.downloadUrl) {
|
|
16369
|
+
// If it's an object with downloadUrl, use that
|
|
16370
|
+
return attachment.downloadUrl;
|
|
16371
|
+
} else if (attachment === null || attachment === void 0 ? void 0 : attachment.url) {
|
|
16372
|
+
// Fallback to url property
|
|
16373
|
+
return attachment.url;
|
|
16374
|
+
}
|
|
16375
|
+
// If it's an object with id, we'll need to keep it for backward compatibility
|
|
16376
|
+
return null;
|
|
16377
|
+
}).filter(url => url !== null);
|
|
16378
|
+
onMessageReceived(messageContent, senderType, needsAgent, attachmentUrls);
|
|
16328
16379
|
} catch (error) {
|
|
16329
|
-
|
|
16380
|
+
console.error('[AblyService] Error processing message', {
|
|
16381
|
+
error,
|
|
16382
|
+
message
|
|
16383
|
+
});
|
|
16330
16384
|
}
|
|
16331
16385
|
});
|
|
16332
16386
|
yield _this2.channel.attach();
|
|
@@ -16356,6 +16410,9 @@ class ClientAblyService {
|
|
|
16356
16410
|
_this3.isConnected = false;
|
|
16357
16411
|
_this3.sessionId = null;
|
|
16358
16412
|
} catch (error) {
|
|
16413
|
+
console.error('[AblyService] Error in stopConnection', {
|
|
16414
|
+
error
|
|
16415
|
+
});
|
|
16359
16416
|
// Reset state even if there's an error
|
|
16360
16417
|
_this3.isConnected = false;
|
|
16361
16418
|
_this3.sessionId = null;
|
|
@@ -16508,19 +16565,21 @@ function useTypewriter(text) {
|
|
|
16508
16565
|
var onType = arguments.length > 2 ? arguments[2] : undefined;
|
|
16509
16566
|
var [displayedText, setDisplayedText] = React.useState('');
|
|
16510
16567
|
React.useEffect(() => {
|
|
16568
|
+
// Ensure text is always a string to prevent errors
|
|
16569
|
+
var safeText = text !== null && text !== void 0 ? text : '';
|
|
16511
16570
|
var index = 0;
|
|
16512
16571
|
setDisplayedText('');
|
|
16513
16572
|
var interval = setInterval(() => {
|
|
16514
16573
|
setDisplayedText(() => {
|
|
16515
|
-
var next =
|
|
16574
|
+
var next = safeText.slice(0, index + 1);
|
|
16516
16575
|
index++;
|
|
16517
16576
|
if (onType) onType();
|
|
16518
|
-
if (index >=
|
|
16577
|
+
if (index >= safeText.length) clearInterval(interval);
|
|
16519
16578
|
return next;
|
|
16520
16579
|
});
|
|
16521
16580
|
}, speed);
|
|
16522
16581
|
return () => clearInterval(interval);
|
|
16523
|
-
}, [text]);
|
|
16582
|
+
}, [text, onType]);
|
|
16524
16583
|
return displayedText;
|
|
16525
16584
|
}
|
|
16526
16585
|
|
|
@@ -34307,11 +34366,13 @@ var AgentResponse = _ref => {
|
|
|
34307
34366
|
messageId,
|
|
34308
34367
|
onType
|
|
34309
34368
|
} = _ref;
|
|
34369
|
+
// Ensure messageContent is always a string to prevent errors
|
|
34370
|
+
var safeMessageContent = messageContent !== null && messageContent !== void 0 ? messageContent : '';
|
|
34310
34371
|
var shouldAnimate = (senderType === 2 || senderType === 3) && !seenMessagesRef.has(messageId);
|
|
34311
|
-
var animatedText = useTypewriter(
|
|
34312
|
-
var finalMessage = shouldAnimate ? animatedText :
|
|
34372
|
+
var animatedText = useTypewriter(safeMessageContent, 20, onType);
|
|
34373
|
+
var finalMessage = shouldAnimate ? animatedText : safeMessageContent;
|
|
34313
34374
|
// Mark message as "seen" after full animation
|
|
34314
|
-
if (shouldAnimate && finalMessage ===
|
|
34375
|
+
if (shouldAnimate && finalMessage === safeMessageContent) {
|
|
34315
34376
|
seenMessagesRef.add(messageId);
|
|
34316
34377
|
}
|
|
34317
34378
|
return jsxRuntime.jsx("div", {
|
|
@@ -34461,6 +34522,32 @@ var ImagePreviewDialog = _ref => {
|
|
|
34461
34522
|
y: 0
|
|
34462
34523
|
});
|
|
34463
34524
|
}, []);
|
|
34525
|
+
var handleDownload = React.useCallback(/*#__PURE__*/_asyncToGenerator(function* () {
|
|
34526
|
+
if (!currentImageUrl) return;
|
|
34527
|
+
try {
|
|
34528
|
+
// Fetch the image as a blob
|
|
34529
|
+
var response = yield fetch(currentImageUrl);
|
|
34530
|
+
var blob = yield response.blob();
|
|
34531
|
+
// Create a temporary URL for the blob
|
|
34532
|
+
var blobUrl = URL.createObjectURL(blob);
|
|
34533
|
+
// Extract filename from URL or use a default
|
|
34534
|
+
var urlParts = currentImageUrl.split('/');
|
|
34535
|
+
var filename = urlParts[urlParts.length - 1].split('?')[0] || 'image.png';
|
|
34536
|
+
// Create a temporary anchor element and trigger download
|
|
34537
|
+
var link = document.createElement('a');
|
|
34538
|
+
link.href = blobUrl;
|
|
34539
|
+
link.download = filename;
|
|
34540
|
+
document.body.appendChild(link);
|
|
34541
|
+
link.click();
|
|
34542
|
+
// Cleanup
|
|
34543
|
+
document.body.removeChild(link);
|
|
34544
|
+
URL.revokeObjectURL(blobUrl);
|
|
34545
|
+
} catch (error) {
|
|
34546
|
+
console.error('Failed to download image:', error);
|
|
34547
|
+
// Fallback: open in new tab if download fails
|
|
34548
|
+
window.open(currentImageUrl, '_blank');
|
|
34549
|
+
}
|
|
34550
|
+
}), [currentImageUrl]);
|
|
34464
34551
|
var handleClose = React.useCallback(() => {
|
|
34465
34552
|
setZoomLevel(1);
|
|
34466
34553
|
setImagePosition({
|
|
@@ -34661,6 +34748,27 @@ var ImagePreviewDialog = _ref => {
|
|
|
34661
34748
|
"aria-label": 'Reset zoom',
|
|
34662
34749
|
type: 'button',
|
|
34663
34750
|
children: "Reset"
|
|
34751
|
+
}), jsxRuntime.jsx("div", {
|
|
34752
|
+
className: 'babylai-h-9 babylai-w-px babylai-bg-white/20 babylai-mx-1'
|
|
34753
|
+
}), jsxRuntime.jsx(Button, {
|
|
34754
|
+
variant: 'ghost',
|
|
34755
|
+
size: 'icon',
|
|
34756
|
+
onClick: handleDownload,
|
|
34757
|
+
className: 'babylai-text-white hover:babylai-text-white/80 hover:babylai-bg-white/10 babylai-h-9 babylai-w-9',
|
|
34758
|
+
"aria-label": 'Download image',
|
|
34759
|
+
type: 'button',
|
|
34760
|
+
children: jsxRuntime.jsx("svg", {
|
|
34761
|
+
className: 'babylai-w-5 babylai-h-5',
|
|
34762
|
+
fill: 'none',
|
|
34763
|
+
stroke: 'currentColor',
|
|
34764
|
+
viewBox: '0 0 24 24',
|
|
34765
|
+
children: jsxRuntime.jsx("path", {
|
|
34766
|
+
strokeLinecap: 'round',
|
|
34767
|
+
strokeLinejoin: 'round',
|
|
34768
|
+
strokeWidth: 2,
|
|
34769
|
+
d: 'M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-4l-4 4m0 0l-4-4m4 4V4'
|
|
34770
|
+
})
|
|
34771
|
+
})
|
|
34664
34772
|
})]
|
|
34665
34773
|
}), hasMultipleImages && jsxRuntime.jsx("div", {
|
|
34666
34774
|
className: cn('babylai-absolute babylai-top-4 babylai-z-[60]', 'babylai-bg-black/50 babylai-backdrop-blur-sm babylai-rounded-lg babylai-px-4 babylai-py-2', dir === 'rtl' ? 'babylai-right-1/2 babylai-translate-x-1/2' : 'babylai-left-1/2 -babylai-translate-x-1/2'),
|
|
@@ -34694,6 +34802,7 @@ ImagePreviewDialog.displayName = 'ImagePreviewDialog';
|
|
|
34694
34802
|
var ImageAttachment = _ref => {
|
|
34695
34803
|
var {
|
|
34696
34804
|
fileId,
|
|
34805
|
+
imageUrl: propImageUrl,
|
|
34697
34806
|
className,
|
|
34698
34807
|
enablePreview = true,
|
|
34699
34808
|
onClick
|
|
@@ -34701,30 +34810,39 @@ var ImageAttachment = _ref => {
|
|
|
34701
34810
|
var {
|
|
34702
34811
|
i18n
|
|
34703
34812
|
} = useLocalTranslation();
|
|
34704
|
-
var [imageUrl, setImageUrl] = React.useState(null);
|
|
34705
|
-
var [loading, setLoading] = React.useState(
|
|
34813
|
+
var [imageUrl, setImageUrl] = React.useState(propImageUrl || null);
|
|
34814
|
+
var [loading, setLoading] = React.useState(!propImageUrl && !!fileId);
|
|
34706
34815
|
var [error, setError] = React.useState(false);
|
|
34707
34816
|
var [isPreviewOpen, setIsPreviewOpen] = React.useState(false);
|
|
34708
34817
|
React.useEffect(() => {
|
|
34709
|
-
|
|
34710
|
-
|
|
34711
|
-
|
|
34712
|
-
|
|
34713
|
-
|
|
34714
|
-
|
|
34715
|
-
|
|
34716
|
-
|
|
34717
|
-
|
|
34718
|
-
|
|
34719
|
-
|
|
34720
|
-
|
|
34721
|
-
|
|
34722
|
-
|
|
34723
|
-
|
|
34724
|
-
|
|
34725
|
-
|
|
34726
|
-
|
|
34727
|
-
|
|
34818
|
+
// If we have a direct URL, use it immediately
|
|
34819
|
+
if (propImageUrl) {
|
|
34820
|
+
setImageUrl(propImageUrl);
|
|
34821
|
+
setLoading(false);
|
|
34822
|
+
return;
|
|
34823
|
+
}
|
|
34824
|
+
// If we only have a fileId, fetch the URL using presignDownload
|
|
34825
|
+
if (fileId) {
|
|
34826
|
+
var fetchImageUrl = /*#__PURE__*/function () {
|
|
34827
|
+
var _ref2 = _asyncToGenerator(function* () {
|
|
34828
|
+
try {
|
|
34829
|
+
setLoading(true);
|
|
34830
|
+
setError(false);
|
|
34831
|
+
var response = yield presignDownload(fileId, i18n.language);
|
|
34832
|
+
setImageUrl(response.downloadUrl);
|
|
34833
|
+
} catch (err) {
|
|
34834
|
+
setError(true);
|
|
34835
|
+
} finally {
|
|
34836
|
+
setLoading(false);
|
|
34837
|
+
}
|
|
34838
|
+
});
|
|
34839
|
+
return function fetchImageUrl() {
|
|
34840
|
+
return _ref2.apply(this, arguments);
|
|
34841
|
+
};
|
|
34842
|
+
}();
|
|
34843
|
+
fetchImageUrl();
|
|
34844
|
+
}
|
|
34845
|
+
}, [fileId, propImageUrl, i18n.language]);
|
|
34728
34846
|
var handleImageClick = () => {
|
|
34729
34847
|
if (onClick) {
|
|
34730
34848
|
onClick();
|
|
@@ -38276,39 +38394,63 @@ var ChatWindowFooter = props => {
|
|
|
38276
38394
|
var _a;
|
|
38277
38395
|
(_a = fileInputRef.current) === null || _a === void 0 ? void 0 : _a.click();
|
|
38278
38396
|
}, []);
|
|
38279
|
-
var handleFileSelect = React.useCallback(
|
|
38280
|
-
var
|
|
38281
|
-
|
|
38282
|
-
|
|
38283
|
-
|
|
38284
|
-
|
|
38285
|
-
|
|
38286
|
-
|
|
38287
|
-
|
|
38288
|
-
|
|
38289
|
-
|
|
38290
|
-
|
|
38291
|
-
|
|
38292
|
-
|
|
38293
|
-
|
|
38294
|
-
|
|
38295
|
-
|
|
38296
|
-
|
|
38397
|
+
var handleFileSelect = React.useCallback(e => {
|
|
38398
|
+
var files = Array.from(e.target.files || []);
|
|
38399
|
+
// Validate that all files are images
|
|
38400
|
+
var imageFiles = files.filter(file => file.type.startsWith('image/'));
|
|
38401
|
+
// Create preview URLs and add to selected files (don't upload yet)
|
|
38402
|
+
var newFiles = imageFiles.map(file => ({
|
|
38403
|
+
file,
|
|
38404
|
+
previewUrl: URL.createObjectURL(file),
|
|
38405
|
+
uploading: false,
|
|
38406
|
+
uploadedId: null,
|
|
38407
|
+
error: null
|
|
38408
|
+
}));
|
|
38409
|
+
setSelectedFiles(prev => [...prev, ...newFiles]);
|
|
38410
|
+
// Clear the input
|
|
38411
|
+
if (fileInputRef.current) {
|
|
38412
|
+
fileInputRef.current.value = '';
|
|
38413
|
+
}
|
|
38414
|
+
// Don't upload files immediately - wait for send button click
|
|
38415
|
+
}, []);
|
|
38416
|
+
// Removed handleUploadFiles - files are now uploaded in handleSendMessageWithAttachments
|
|
38417
|
+
var handleRemoveFile = React.useCallback(previewUrl => {
|
|
38418
|
+
setSelectedFiles(prev => {
|
|
38419
|
+
var fileToRemove = prev.find(f => f.previewUrl === previewUrl);
|
|
38420
|
+
if (fileToRemove) {
|
|
38421
|
+
URL.revokeObjectURL(fileToRemove.previewUrl);
|
|
38297
38422
|
}
|
|
38298
|
-
|
|
38299
|
-
yield handleUploadFiles(newFiles);
|
|
38423
|
+
return prev.filter(f => f.previewUrl !== previewUrl);
|
|
38300
38424
|
});
|
|
38301
|
-
|
|
38302
|
-
|
|
38303
|
-
|
|
38304
|
-
|
|
38305
|
-
|
|
38306
|
-
|
|
38307
|
-
|
|
38308
|
-
|
|
38425
|
+
}, []);
|
|
38426
|
+
var handleSendMessageWithAttachments = React.useCallback(/*#__PURE__*/_asyncToGenerator(function* () {
|
|
38427
|
+
// Prevent sending if already loading
|
|
38428
|
+
if (props.isLoading) {
|
|
38429
|
+
return;
|
|
38430
|
+
}
|
|
38431
|
+
// Get files that need to be uploaded (those without uploadedId)
|
|
38432
|
+
var filesToUpload = selectedFiles.filter(f => f.uploadedId === null && !f.error);
|
|
38433
|
+
var alreadyUploadedIds = selectedFiles.filter(f => f.uploadedId !== null).map(f => f.uploadedId);
|
|
38434
|
+
// Declare uploadedIds outside the if block so it's accessible later
|
|
38435
|
+
var uploadedIds = [];
|
|
38436
|
+
// If there are files to upload, upload them first
|
|
38437
|
+
if (filesToUpload.length > 0) {
|
|
38438
|
+
// Get session ID - only use existing, never create new one
|
|
38439
|
+
var sessionId = null;
|
|
38309
38440
|
try {
|
|
38310
|
-
sessionId
|
|
38441
|
+
// Only use existing sessionId, never call onEnsureSession
|
|
38442
|
+
if (props.sessionId) {
|
|
38443
|
+
sessionId = props.sessionId;
|
|
38444
|
+
} else {
|
|
38445
|
+
// Mark all files as error
|
|
38446
|
+
setSelectedFiles(prev => prev.map(f => filesToUpload.some(ftl => ftl.previewUrl === f.previewUrl) ? _objectSpread2(_objectSpread2({}, f), {}, {
|
|
38447
|
+
error: 'No session available',
|
|
38448
|
+
uploading: false
|
|
38449
|
+
}) : f));
|
|
38450
|
+
return;
|
|
38451
|
+
}
|
|
38311
38452
|
} catch (error) {
|
|
38453
|
+
console.error('[ChatWindowFooter] Failed to get sessionId for file upload:', error);
|
|
38312
38454
|
// Mark all files as error
|
|
38313
38455
|
setSelectedFiles(prev => prev.map(f => filesToUpload.some(ftl => ftl.previewUrl === f.previewUrl) ? _objectSpread2(_objectSpread2({}, f), {}, {
|
|
38314
38456
|
error: 'Failed to initialize session',
|
|
@@ -38316,7 +38458,9 @@ var ChatWindowFooter = props => {
|
|
|
38316
38458
|
}) : f));
|
|
38317
38459
|
return;
|
|
38318
38460
|
}
|
|
38319
|
-
// Upload each file
|
|
38461
|
+
// Upload each file and collect uploaded IDs
|
|
38462
|
+
uploadedIds = [];
|
|
38463
|
+
var hasUploadErrors = false;
|
|
38320
38464
|
var _loop = function* _loop(fileDto) {
|
|
38321
38465
|
try {
|
|
38322
38466
|
// Mark as uploading
|
|
@@ -38327,7 +38471,6 @@ var ChatWindowFooter = props => {
|
|
|
38327
38471
|
// Get presigned URL
|
|
38328
38472
|
var presignResponse = yield presignUpload(sessionId, fileDto.file, i18n.language);
|
|
38329
38473
|
// Upload file to presigned URL using axios
|
|
38330
|
-
// Important: Content-Type must match the file type (e.g., 'image/png'), not 'multipart/form-data'
|
|
38331
38474
|
var uploadResponse = yield axios$1.put(presignResponse.uploadUrl, fileDto.file, {
|
|
38332
38475
|
headers: {
|
|
38333
38476
|
'Content-Type': fileDto.file.type
|
|
@@ -38339,6 +38482,8 @@ var ChatWindowFooter = props => {
|
|
|
38339
38482
|
if (uploadResponse.status !== 200 && uploadResponse.status !== 204) {
|
|
38340
38483
|
throw new Error("Upload failed with status ".concat(uploadResponse.status));
|
|
38341
38484
|
}
|
|
38485
|
+
// Collect uploaded ID
|
|
38486
|
+
uploadedIds.push(presignResponse.id);
|
|
38342
38487
|
// Update with uploaded ID
|
|
38343
38488
|
setSelectedFiles(prev => prev.map(f => f.previewUrl === fileDto.previewUrl ? _objectSpread2(_objectSpread2({}, f), {}, {
|
|
38344
38489
|
uploading: false,
|
|
@@ -38346,6 +38491,8 @@ var ChatWindowFooter = props => {
|
|
|
38346
38491
|
error: null
|
|
38347
38492
|
}) : f));
|
|
38348
38493
|
} catch (error) {
|
|
38494
|
+
console.error('[ChatWindowFooter] File upload failed:', error);
|
|
38495
|
+
hasUploadErrors = true;
|
|
38349
38496
|
setSelectedFiles(prev => prev.map(f => f.previewUrl === fileDto.previewUrl ? _objectSpread2(_objectSpread2({}, f), {}, {
|
|
38350
38497
|
uploading: false,
|
|
38351
38498
|
error: 'Upload failed',
|
|
@@ -38356,43 +38503,28 @@ var ChatWindowFooter = props => {
|
|
|
38356
38503
|
for (var fileDto of filesToUpload) {
|
|
38357
38504
|
yield* _loop(fileDto);
|
|
38358
38505
|
}
|
|
38359
|
-
|
|
38360
|
-
|
|
38361
|
-
|
|
38362
|
-
|
|
38363
|
-
}(), [props.onEnsureSession, i18n.language]);
|
|
38364
|
-
var handleRemoveFile = React.useCallback(previewUrl => {
|
|
38365
|
-
setSelectedFiles(prev => {
|
|
38366
|
-
var fileToRemove = prev.find(f => f.previewUrl === previewUrl);
|
|
38367
|
-
if (fileToRemove) {
|
|
38368
|
-
URL.revokeObjectURL(fileToRemove.previewUrl);
|
|
38506
|
+
// If any uploads failed, don't send the message
|
|
38507
|
+
if (hasUploadErrors) {
|
|
38508
|
+
console.error('[ChatWindowFooter] Some files failed to upload, not sending message');
|
|
38509
|
+
return;
|
|
38369
38510
|
}
|
|
38370
|
-
|
|
38371
|
-
|
|
38372
|
-
|
|
38373
|
-
|
|
38374
|
-
// Only allow sending if all files have finished uploading (either successfully or with error)
|
|
38375
|
-
var hasUploadingFiles = selectedFiles.some(f => f.uploading);
|
|
38376
|
-
if (hasUploadingFiles) {
|
|
38377
|
-
return; // Prevent sending if any files are still uploading
|
|
38378
|
-
}
|
|
38379
|
-
// Get all successfully uploaded file IDs
|
|
38380
|
-
var attachmentIds = selectedFiles.filter(f => f.uploadedId !== null).map(f => f.uploadedId);
|
|
38511
|
+
}
|
|
38512
|
+
// Get all successfully uploaded file IDs (already uploaded + newly uploaded)
|
|
38513
|
+
// Use uploadedIds from the upload loop instead of reading from state
|
|
38514
|
+
var allAttachmentIds = [...alreadyUploadedIds, ...uploadedIds];
|
|
38381
38515
|
// Call the original send message with attachment IDs
|
|
38382
|
-
props.handleSendMessage(
|
|
38516
|
+
props.handleSendMessage(allAttachmentIds);
|
|
38383
38517
|
// Clear selected files and revoke URLs
|
|
38384
38518
|
selectedFiles.forEach(f => URL.revokeObjectURL(f.previewUrl));
|
|
38385
38519
|
setSelectedFiles([]);
|
|
38386
|
-
}, [selectedFiles, props]);
|
|
38520
|
+
}), [selectedFiles, props, i18n.language]);
|
|
38387
38521
|
// Check if any files are currently uploading
|
|
38388
38522
|
var hasUploadingFiles = selectedFiles.some(f => f.uploading);
|
|
38389
|
-
// Check if there are files
|
|
38390
|
-
|
|
38391
|
-
|
|
38392
|
-
|
|
38393
|
-
var
|
|
38394
|
-
var allFilesHaveErrors = selectedFiles.length > 0 && !hasSuccessfulUploads && !hasUploadingFiles && !hasPendingFiles;
|
|
38395
|
-
var isSendDisabled = props.isLoading || props.inputMessage.trim() === '' || hasUploadingFiles || hasPendingFiles || allFilesHaveErrors;
|
|
38523
|
+
// Check if there are files with errors
|
|
38524
|
+
var hasFileErrors = selectedFiles.some(f => f.error !== null);
|
|
38525
|
+
// Allow sending if there's text OR files selected (files will be uploaded on send)
|
|
38526
|
+
var hasContentToSend = props.inputMessage.trim() !== '' || selectedFiles.length > 0;
|
|
38527
|
+
var isSendDisabled = props.isLoading || !hasContentToSend || hasUploadingFiles || hasFileErrors;
|
|
38396
38528
|
var handleKeyDown = React.useCallback(e => {
|
|
38397
38529
|
if (e.key === 'Enter' && !e.shiftKey) {
|
|
38398
38530
|
e.preventDefault();
|
|
@@ -38557,10 +38689,12 @@ var MessageComponent = /*#__PURE__*/React__default["default"].memo(_ref => {
|
|
|
38557
38689
|
var isFirstHumanAgentMessage = index === firstHumanAgentIndex && message.senderType === 2;
|
|
38558
38690
|
var textDirection = message.senderType === 1 ? 'babylai-justify-end' : 'babylai-justify-start';
|
|
38559
38691
|
var handleImageClick = React.useCallback(clickedIndex => {
|
|
38560
|
-
if (
|
|
38561
|
-
|
|
38692
|
+
// Use attachmentUrls if available (from Ably), otherwise use attachmentIds (user-sent)
|
|
38693
|
+
var attachments = message.attachmentUrls || message.attachmentIds || [];
|
|
38694
|
+
if (attachments.length > 0) {
|
|
38695
|
+
onImageClick(attachments, clickedIndex);
|
|
38562
38696
|
}
|
|
38563
|
-
}, [message.attachmentIds, onImageClick]);
|
|
38697
|
+
}, [message.attachmentIds, message.attachmentUrls, onImageClick]);
|
|
38564
38698
|
return jsxRuntime.jsxs("div", {
|
|
38565
38699
|
children: [isFirstHumanAgentMessage && jsxRuntime.jsx("div", {
|
|
38566
38700
|
className: 'babylai-flex babylai-justify-center babylai-items-center babylai-my-4',
|
|
@@ -38583,14 +38717,21 @@ var MessageComponent = /*#__PURE__*/React__default["default"].memo(_ref => {
|
|
|
38583
38717
|
className: 'babylai-flex-shrink-0 babylai-me-3 babylai-w-8'
|
|
38584
38718
|
}), jsxRuntime.jsxs("div", {
|
|
38585
38719
|
className: 'babylai-flex babylai-flex-col babylai-gap-2',
|
|
38586
|
-
children: [message.
|
|
38720
|
+
children: [message.attachmentUrls && message.attachmentUrls.length > 0 && jsxRuntime.jsx("div", {
|
|
38721
|
+
className: 'babylai-flex babylai-flex-row babylai-flex-wrap babylai-gap-2 babylai-max-w-full',
|
|
38722
|
+
children: message.attachmentUrls.map((attachmentUrl, imgIndex) => jsxRuntime.jsx(ImageAttachment, {
|
|
38723
|
+
imageUrl: attachmentUrl,
|
|
38724
|
+
enablePreview: false,
|
|
38725
|
+
onClick: () => handleImageClick(imgIndex)
|
|
38726
|
+
}, attachmentUrl))
|
|
38727
|
+
}), message.attachmentIds && message.attachmentIds.length > 0 && jsxRuntime.jsx("div", {
|
|
38587
38728
|
className: 'babylai-flex babylai-flex-row babylai-flex-wrap babylai-gap-2 babylai-max-w-full',
|
|
38588
38729
|
children: message.attachmentIds.map((attachmentId, imgIndex) => jsxRuntime.jsx(ImageAttachment, {
|
|
38589
38730
|
fileId: attachmentId,
|
|
38590
38731
|
enablePreview: false,
|
|
38591
38732
|
onClick: () => handleImageClick(imgIndex)
|
|
38592
38733
|
}, attachmentId))
|
|
38593
|
-
}), jsxRuntime.jsx(AgentResponse$1, {
|
|
38734
|
+
}), message.messageContent && message.messageContent.trim() !== '' && jsxRuntime.jsx(AgentResponse$1, {
|
|
38594
38735
|
messageContent: message.messageContent,
|
|
38595
38736
|
senderType: message.senderType,
|
|
38596
38737
|
messageId: message.id,
|
|
@@ -38635,7 +38776,8 @@ var ChatWindow = /*#__PURE__*/React__default["default"].memo(_ref3 => {
|
|
|
38635
38776
|
onEnsureSession,
|
|
38636
38777
|
messages,
|
|
38637
38778
|
assistantStatus = 'loading',
|
|
38638
|
-
needsAgent
|
|
38779
|
+
needsAgent,
|
|
38780
|
+
sessionId
|
|
38639
38781
|
} = _ref3;
|
|
38640
38782
|
var {
|
|
38641
38783
|
i18n
|
|
@@ -38678,7 +38820,8 @@ var ChatWindow = /*#__PURE__*/React__default["default"].memo(_ref3 => {
|
|
|
38678
38820
|
};
|
|
38679
38821
|
}, []);
|
|
38680
38822
|
var handleSendMessage = React.useCallback(attachmentIds => {
|
|
38681
|
-
if
|
|
38823
|
+
// Allow sending if there's text OR attachments
|
|
38824
|
+
if (inputMessage.trim() || attachmentIds.length > 0) {
|
|
38682
38825
|
onSendMessage(inputMessage, attachmentIds);
|
|
38683
38826
|
setInputMessage('');
|
|
38684
38827
|
}
|
|
@@ -38689,13 +38832,32 @@ var ChatWindow = /*#__PURE__*/React__default["default"].memo(_ref3 => {
|
|
|
38689
38832
|
}, [messages]);
|
|
38690
38833
|
// Handle image gallery opening
|
|
38691
38834
|
var handleImageClick = React.useCallback(/*#__PURE__*/function () {
|
|
38692
|
-
var _ref4 = _asyncToGenerator(function* (
|
|
38693
|
-
|
|
38835
|
+
var _ref4 = _asyncToGenerator(function* (attachmentIdsOrUrls, clickedIndex) {
|
|
38836
|
+
var _a, _b;
|
|
38837
|
+
if (!attachmentIdsOrUrls || attachmentIdsOrUrls.length === 0) {
|
|
38694
38838
|
return;
|
|
38695
38839
|
}
|
|
38696
38840
|
try {
|
|
38697
|
-
//
|
|
38698
|
-
|
|
38841
|
+
// Check if the first item is a URL (starts with http:// or https://)
|
|
38842
|
+
// If so, they're all URLs from Ably and can be used directly
|
|
38843
|
+
var isUrl = ((_a = attachmentIdsOrUrls[0]) === null || _a === void 0 ? void 0 : _a.startsWith('http://')) || ((_b = attachmentIdsOrUrls[0]) === null || _b === void 0 ? void 0 : _b.startsWith('https://'));
|
|
38844
|
+
var imageUrls;
|
|
38845
|
+
if (isUrl) {
|
|
38846
|
+
// These are already URLs from Ably, use them directly (no async needed)
|
|
38847
|
+
imageUrls = attachmentIdsOrUrls.filter(url => url !== null && url.length > 0);
|
|
38848
|
+
// Open gallery immediately with URLs
|
|
38849
|
+
if (imageUrls.length > 0) {
|
|
38850
|
+
var _adjustedIndex = Math.max(0, Math.min(clickedIndex, imageUrls.length - 1));
|
|
38851
|
+
setGalleryState({
|
|
38852
|
+
isOpen: true,
|
|
38853
|
+
imageUrls,
|
|
38854
|
+
initialIndex: _adjustedIndex
|
|
38855
|
+
});
|
|
38856
|
+
}
|
|
38857
|
+
return; // Exit early since we don't need to fetch anything
|
|
38858
|
+
}
|
|
38859
|
+
// These are file IDs, need to fetch URLs using presignDownload
|
|
38860
|
+
var imageUrlPromises = attachmentIdsOrUrls.map(fileId => {
|
|
38699
38861
|
if (!fileId || typeof fileId !== 'string') {
|
|
38700
38862
|
return Promise.resolve(null);
|
|
38701
38863
|
}
|
|
@@ -38709,7 +38871,7 @@ var ChatWindow = /*#__PURE__*/React__default["default"].memo(_ref3 => {
|
|
|
38709
38871
|
return null;
|
|
38710
38872
|
});
|
|
38711
38873
|
});
|
|
38712
|
-
|
|
38874
|
+
imageUrls = (yield Promise.all(imageUrlPromises)).filter(url => url !== null && url.length > 0);
|
|
38713
38875
|
if (imageUrls.length === 0) {
|
|
38714
38876
|
return;
|
|
38715
38877
|
}
|
|
@@ -38771,7 +38933,8 @@ var ChatWindow = /*#__PURE__*/React__default["default"].memo(_ref3 => {
|
|
|
38771
38933
|
handleSendMessage: handleSendMessage,
|
|
38772
38934
|
setInputMessage: setInputMessage,
|
|
38773
38935
|
isLoading: isLoading,
|
|
38774
|
-
onEnsureSession: onEnsureSession
|
|
38936
|
+
onEnsureSession: onEnsureSession,
|
|
38937
|
+
sessionId: sessionId
|
|
38775
38938
|
}), galleryState.isOpen && galleryState.imageUrls.length > 0 && jsxRuntime.jsx(ImagePreviewDialog, {
|
|
38776
38939
|
imageUrls: galleryState.imageUrls,
|
|
38777
38940
|
initialIndex: galleryState.initialIndex,
|
|
@@ -39390,7 +39553,8 @@ var HelpPopup = _ref => {
|
|
|
39390
39553
|
}
|
|
39391
39554
|
}, [onStartChat, setSelectedOption, sessionId, setStartNewChatConfirmation, setTempSelectedOption]);
|
|
39392
39555
|
var handleSendMessage = React.useCallback((message, attachmentIds) => {
|
|
39393
|
-
if
|
|
39556
|
+
// Allow sending if there's text OR attachments
|
|
39557
|
+
if (message.trim() || attachmentIds.length > 0) {
|
|
39394
39558
|
onSendMessage(message.trim(), attachmentIds);
|
|
39395
39559
|
}
|
|
39396
39560
|
}, [onSendMessage]);
|
|
@@ -39446,7 +39610,8 @@ var HelpPopup = _ref => {
|
|
|
39446
39610
|
messages: memoizedMessages,
|
|
39447
39611
|
assistantStatus: assistantStatus,
|
|
39448
39612
|
needsAgent: needsAgent,
|
|
39449
|
-
isAblyConnected: isAblyConnected
|
|
39613
|
+
isAblyConnected: isAblyConnected,
|
|
39614
|
+
sessionId: sessionId
|
|
39450
39615
|
})]
|
|
39451
39616
|
});
|
|
39452
39617
|
}
|
|
@@ -39585,7 +39750,7 @@ var HelpCenterContent = _ref => {
|
|
|
39585
39750
|
sentAt: new Date(),
|
|
39586
39751
|
isSeen: true
|
|
39587
39752
|
}, attachments.length > 0 && {
|
|
39588
|
-
|
|
39753
|
+
attachmentUrls: attachments
|
|
39589
39754
|
});
|
|
39590
39755
|
return [...prevMessages, newMessage];
|
|
39591
39756
|
});
|
|
@@ -39714,10 +39879,11 @@ var HelpCenterContent = _ref => {
|
|
|
39714
39879
|
var handleEnsureSession = /*#__PURE__*/function () {
|
|
39715
39880
|
var _ref7 = _asyncToGenerator(function* () {
|
|
39716
39881
|
// If we already have a session ID and connection, return it
|
|
39717
|
-
if
|
|
39882
|
+
// NEVER create a new session if one already exists
|
|
39883
|
+
if (sessionId) {
|
|
39718
39884
|
return sessionId;
|
|
39719
39885
|
}
|
|
39720
|
-
//
|
|
39886
|
+
// Only create a new session if we don't have one and have a selected option
|
|
39721
39887
|
if (selectedOption) {
|
|
39722
39888
|
var newSessionId = yield startNewChatSession(selectedOption);
|
|
39723
39889
|
return newSessionId;
|
|
@@ -39731,28 +39897,32 @@ var HelpCenterContent = _ref => {
|
|
|
39731
39897
|
var handleSendMessage = /*#__PURE__*/function () {
|
|
39732
39898
|
var _ref8 = _asyncToGenerator(function* (message) {
|
|
39733
39899
|
var attachmentIds = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : [];
|
|
39734
|
-
|
|
39900
|
+
// Allow sending if there's text OR attachments
|
|
39901
|
+
if (message.trim() !== '' || attachmentIds.length > 0) {
|
|
39735
39902
|
try {
|
|
39736
39903
|
setAssistantStatus('typing');
|
|
39737
39904
|
var userMessage = {
|
|
39738
39905
|
id: Date.now(),
|
|
39739
39906
|
senderType: 1,
|
|
39740
|
-
messageContent: message,
|
|
39907
|
+
messageContent: message || '',
|
|
39908
|
+
// Use empty string if message is empty but attachments exist
|
|
39741
39909
|
sentAt: new Date(),
|
|
39742
39910
|
isSeen: false,
|
|
39743
39911
|
attachmentIds: attachmentIds.length > 0 ? attachmentIds : undefined
|
|
39744
39912
|
};
|
|
39745
39913
|
setMessages(prevMessages => [...prevMessages, userMessage]);
|
|
39746
|
-
// Handle session creation if needed
|
|
39914
|
+
// Handle session creation if needed - only create if no session exists
|
|
39747
39915
|
var currentSessionId = sessionId;
|
|
39748
|
-
if
|
|
39916
|
+
// Only create a new session if we don't have one and we have a selected option
|
|
39917
|
+
// This ensures session is only created once with the first message
|
|
39918
|
+
if (!currentSessionId && !isAblyConnected && selectedOption) {
|
|
39749
39919
|
currentSessionId = yield startNewChatSession(selectedOption);
|
|
39750
39920
|
}
|
|
39751
39921
|
if (!currentSessionId) {
|
|
39752
39922
|
throw new Error('No active session available');
|
|
39753
39923
|
}
|
|
39754
39924
|
var messageDto = _objectSpread2({
|
|
39755
|
-
messageContent: message
|
|
39925
|
+
messageContent: message || ''
|
|
39756
39926
|
}, attachmentIds.length > 0 && {
|
|
39757
39927
|
attachmentIds
|
|
39758
39928
|
});
|