@aslaluroba/help-center-react 3.0.21 → 3.2.1
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 +9 -0
- package/dist/components/ui/image-preview-dialog.d.ts +10 -0
- package/dist/components/ui/index.d.ts +4 -0
- package/dist/core/api.d.ts +3 -1
- package/dist/index.css +1 -1
- package/dist/index.esm.js +5332 -843
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +5332 -843
- package/dist/index.js.map +1 -1
- package/dist/lib/types.d.ts +23 -0
- package/dist/services.esm.js +492 -361
- package/dist/services.esm.js.map +1 -1
- package/dist/services.js +492 -361
- package/dist/services.js.map +1 -1
- package/dist/ui/chatbot-popup/chat-window-screen/footer.d.ts +3 -2
- package/dist/ui/chatbot-popup/chat-window-screen/index.d.ts +3 -2
- package/dist/ui/help-center.d.ts +1 -1
- package/dist/ui/help-popup.d.ts +3 -2
- package/package.json +1 -1
- package/src/.DS_Store +0 -0
- package/src/assets/icons/paperclip.svg +3 -0
- package/src/assets/icons/x.svg +4 -0
- package/src/components/ui/image-attachment.tsx +107 -0
- package/src/components/ui/image-preview-dialog.tsx +354 -0
- package/src/components/ui/index.ts +4 -0
- package/src/core/AblyService.ts +6 -3
- package/src/core/ApiService.ts +0 -2
- package/src/core/api.ts +106 -58
- package/src/lib/types.ts +110 -84
- package/src/ui/chatbot-popup/chat-window-screen/footer.tsx +259 -22
- package/src/ui/chatbot-popup/chat-window-screen/index.tsx +206 -101
- package/src/ui/help-center.tsx +88 -92
- package/src/ui/help-popup.tsx +11 -9
- package/src/ui/powered-by.tsx +10 -9
|
@@ -1,9 +1,10 @@
|
|
|
1
|
+
import React from 'react';
|
|
1
2
|
interface ChatWindowFooterProps {
|
|
2
3
|
inputMessage: string;
|
|
3
4
|
setInputMessage: (e: string) => void;
|
|
4
|
-
|
|
5
|
-
handleSendMessage: () => void;
|
|
5
|
+
handleSendMessage: (attachmentIds: string[]) => void;
|
|
6
6
|
isLoading: boolean;
|
|
7
|
+
onEnsureSession: () => Promise<string>;
|
|
7
8
|
}
|
|
8
9
|
declare const ChatWindowFooter: React.FC<ChatWindowFooterProps>;
|
|
9
10
|
export default ChatWindowFooter;
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import { Message } from '@/lib/types';
|
|
2
2
|
import React from 'react';
|
|
3
3
|
interface ChatWindowProps {
|
|
4
|
-
onSendMessage: (message: string) => void;
|
|
4
|
+
onSendMessage: (message: string, attachmentIds: string[]) => void;
|
|
5
|
+
onEnsureSession: () => Promise<string>;
|
|
5
6
|
messages: Message[];
|
|
6
7
|
assistantStatus: string;
|
|
7
8
|
needsAgent: boolean;
|
|
8
9
|
isAblyConnected: boolean;
|
|
9
10
|
}
|
|
10
|
-
export declare const ChatWindow: React.MemoExoticComponent<({ onSendMessage, messages, assistantStatus }: ChatWindowProps) => import("react/jsx-runtime").JSX.Element>;
|
|
11
|
+
export declare const ChatWindow: React.MemoExoticComponent<({ onSendMessage, onEnsureSession, messages, assistantStatus, needsAgent }: ChatWindowProps) => import("react/jsx-runtime").JSX.Element>;
|
|
11
12
|
export {};
|
package/dist/ui/help-center.d.ts
CHANGED
package/dist/ui/help-popup.d.ts
CHANGED
|
@@ -7,7 +7,8 @@ type HelpPopupProps = {
|
|
|
7
7
|
error: string | null;
|
|
8
8
|
user: any;
|
|
9
9
|
onStartChat: (option: Option) => void;
|
|
10
|
-
onSendMessage: (message: string) => void;
|
|
10
|
+
onSendMessage: (message: string, attachmentIds: string[]) => void;
|
|
11
|
+
onEnsureSession: () => Promise<string>;
|
|
11
12
|
onEndChat: (option?: Option) => void;
|
|
12
13
|
messages: Message[];
|
|
13
14
|
needsAgent: boolean;
|
|
@@ -20,5 +21,5 @@ type HelpPopupProps = {
|
|
|
20
21
|
showHelpScreen: boolean;
|
|
21
22
|
logoUrl?: string;
|
|
22
23
|
};
|
|
23
|
-
declare const HelpPopup: ({ onClose, helpScreen, status, error, onStartChat, onSendMessage, onEndChat, messages, assistantStatus, needsAgent, sessionId, selectedOption, setSelectedOption, showHelpScreen, isAblyConnected, logoUrl, }: HelpPopupProps) => import("react/jsx-runtime").JSX.Element;
|
|
24
|
+
declare const HelpPopup: ({ onClose, helpScreen, status, error, onStartChat, onSendMessage, onEnsureSession, onEndChat, messages, assistantStatus, needsAgent, sessionId, selectedOption, setSelectedOption, showHelpScreen, isAblyConnected, logoUrl, }: HelpPopupProps) => import("react/jsx-runtime").JSX.Element;
|
|
24
25
|
export default HelpPopup;
|
package/package.json
CHANGED
package/src/.DS_Store
ADDED
|
Binary file
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
2
|
+
<path d="m21.44 11.05-9.19 9.19a6 6 0 0 1-8.49-8.49l8.57-8.57A4 4 0 1 1 18 8.84l-8.59 8.57a2 2 0 0 1-2.83-2.83l8.49-8.48"/>
|
|
3
|
+
</svg>
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import React, { useEffect, useState } from 'react';
|
|
2
|
+
import { presignDownload } from '@/core/api';
|
|
3
|
+
import { cn } from '@/lib/utils';
|
|
4
|
+
import { useLocalTranslation } from '../../useLocalTranslation';
|
|
5
|
+
import { ImagePreviewDialog } from './image-preview-dialog';
|
|
6
|
+
|
|
7
|
+
interface ImageAttachmentProps {
|
|
8
|
+
fileId: string;
|
|
9
|
+
className?: string;
|
|
10
|
+
enablePreview?: boolean;
|
|
11
|
+
onClick?: () => void;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export const ImageAttachment: React.FC<ImageAttachmentProps> = ({
|
|
15
|
+
fileId,
|
|
16
|
+
className,
|
|
17
|
+
enablePreview = true,
|
|
18
|
+
onClick,
|
|
19
|
+
}) => {
|
|
20
|
+
const { i18n } = useLocalTranslation();
|
|
21
|
+
const [imageUrl, setImageUrl] = useState<string | null>(null);
|
|
22
|
+
const [loading, setLoading] = useState(true);
|
|
23
|
+
const [error, setError] = useState(false);
|
|
24
|
+
const [isPreviewOpen, setIsPreviewOpen] = useState(false);
|
|
25
|
+
|
|
26
|
+
useEffect(() => {
|
|
27
|
+
const fetchImageUrl = async () => {
|
|
28
|
+
try {
|
|
29
|
+
setLoading(true);
|
|
30
|
+
setError(false);
|
|
31
|
+
const response = await presignDownload(fileId, i18n.language);
|
|
32
|
+
setImageUrl(response.downloadUrl);
|
|
33
|
+
} catch (err) {
|
|
34
|
+
setError(true);
|
|
35
|
+
} finally {
|
|
36
|
+
setLoading(false);
|
|
37
|
+
}
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
fetchImageUrl();
|
|
41
|
+
}, [fileId, i18n.language]);
|
|
42
|
+
|
|
43
|
+
const handleImageClick = () => {
|
|
44
|
+
if (onClick) {
|
|
45
|
+
onClick();
|
|
46
|
+
} else if (enablePreview && imageUrl) {
|
|
47
|
+
setIsPreviewOpen(true);
|
|
48
|
+
}
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
if (loading) {
|
|
52
|
+
return (
|
|
53
|
+
<div
|
|
54
|
+
className={cn(
|
|
55
|
+
'babylai-flex babylai-items-center babylai-justify-center babylai-bg-black-white-100 babylai-rounded-lg babylai-w-16 babylai-h-16',
|
|
56
|
+
className
|
|
57
|
+
)}
|
|
58
|
+
>
|
|
59
|
+
<div className='babylai-animate-pulse babylai-text-xs babylai-text-black-white-500'>...</div>
|
|
60
|
+
</div>
|
|
61
|
+
);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
if (error || !imageUrl) {
|
|
65
|
+
return (
|
|
66
|
+
<div
|
|
67
|
+
className={cn(
|
|
68
|
+
'babylai-flex babylai-items-center babylai-justify-center babylai-bg-black-white-100 babylai-rounded-lg babylai-w-16 babylai-h-16 babylai-border babylai-border-black-white-200',
|
|
69
|
+
className
|
|
70
|
+
)}
|
|
71
|
+
>
|
|
72
|
+
<div className='babylai-text-xs babylai-text-black-white-500'>!</div>
|
|
73
|
+
</div>
|
|
74
|
+
);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
return (
|
|
78
|
+
<>
|
|
79
|
+
<img
|
|
80
|
+
src={imageUrl}
|
|
81
|
+
alt='Attachment'
|
|
82
|
+
className={cn(
|
|
83
|
+
'babylai-w-16 babylai-h-16 babylai-object-cover babylai-rounded-lg babylai-border babylai-border-black-white-200 babylai-max-w-[50px]',
|
|
84
|
+
(enablePreview || onClick) && 'babylai-cursor-pointer hover:babylai-opacity-80 babylai-transition-opacity',
|
|
85
|
+
className
|
|
86
|
+
)}
|
|
87
|
+
onError={() => setError(true)}
|
|
88
|
+
onClick={handleImageClick}
|
|
89
|
+
role={enablePreview || onClick ? 'button' : undefined}
|
|
90
|
+
aria-label={enablePreview || onClick ? 'Click to preview image' : undefined}
|
|
91
|
+
/>
|
|
92
|
+
{enablePreview && !onClick && (
|
|
93
|
+
<ImagePreviewDialog
|
|
94
|
+
imageUrls={[imageUrl]}
|
|
95
|
+
initialIndex={0}
|
|
96
|
+
isOpen={isPreviewOpen}
|
|
97
|
+
onClose={() => setIsPreviewOpen(false)}
|
|
98
|
+
alt='Attachment preview'
|
|
99
|
+
/>
|
|
100
|
+
)}
|
|
101
|
+
</>
|
|
102
|
+
);
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
ImageAttachment.displayName = 'ImageAttachment';
|
|
106
|
+
|
|
107
|
+
export default ImageAttachment;
|
|
@@ -0,0 +1,354 @@
|
|
|
1
|
+
import React, { useEffect, useCallback, useRef, useState } from 'react';
|
|
2
|
+
import { cn } from '@/lib/utils';
|
|
3
|
+
import { Button } from '@/components';
|
|
4
|
+
import XIcon from '../../assets/icons/x.svg';
|
|
5
|
+
import ArrowRightIcon from '../../assets/icons/arrowRight.svg';
|
|
6
|
+
import { useLocalTranslation } from '../../useLocalTranslation';
|
|
7
|
+
|
|
8
|
+
interface ImagePreviewDialogProps {
|
|
9
|
+
imageUrls: string[];
|
|
10
|
+
initialIndex: number;
|
|
11
|
+
isOpen: boolean;
|
|
12
|
+
onClose: () => void;
|
|
13
|
+
alt?: string;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export const ImagePreviewDialog: React.FC<ImagePreviewDialogProps> = ({
|
|
17
|
+
imageUrls,
|
|
18
|
+
initialIndex,
|
|
19
|
+
isOpen,
|
|
20
|
+
onClose,
|
|
21
|
+
alt = 'Preview',
|
|
22
|
+
}) => {
|
|
23
|
+
const { dir } = useLocalTranslation();
|
|
24
|
+
const [currentIndex, setCurrentIndex] = useState(initialIndex);
|
|
25
|
+
const [zoomLevel, setZoomLevel] = useState(1);
|
|
26
|
+
const [imagePosition, setImagePosition] = useState({ x: 0, y: 0 });
|
|
27
|
+
const [isDragging, setIsDragging] = useState(false);
|
|
28
|
+
const [dragStart, setDragStart] = useState({ x: 0, y: 0 });
|
|
29
|
+
const imageRef = useRef<HTMLImageElement>(null);
|
|
30
|
+
const containerRef = useRef<HTMLDivElement>(null);
|
|
31
|
+
|
|
32
|
+
const currentImageUrl = imageUrls[currentIndex];
|
|
33
|
+
const hasMultipleImages = imageUrls.length > 1;
|
|
34
|
+
|
|
35
|
+
// Update current index when initialIndex changes
|
|
36
|
+
useEffect(() => {
|
|
37
|
+
if (isOpen) {
|
|
38
|
+
setCurrentIndex(initialIndex);
|
|
39
|
+
setZoomLevel(1);
|
|
40
|
+
setImagePosition({ x: 0, y: 0 });
|
|
41
|
+
}
|
|
42
|
+
}, [initialIndex, isOpen]);
|
|
43
|
+
|
|
44
|
+
const handlePrevious = useCallback(() => {
|
|
45
|
+
if (currentIndex > 0) {
|
|
46
|
+
setCurrentIndex(currentIndex - 1);
|
|
47
|
+
setZoomLevel(1);
|
|
48
|
+
setImagePosition({ x: 0, y: 0 });
|
|
49
|
+
}
|
|
50
|
+
}, [currentIndex]);
|
|
51
|
+
|
|
52
|
+
const handleNext = useCallback(() => {
|
|
53
|
+
if (currentIndex < imageUrls.length - 1) {
|
|
54
|
+
setCurrentIndex(currentIndex + 1);
|
|
55
|
+
setZoomLevel(1);
|
|
56
|
+
setImagePosition({ x: 0, y: 0 });
|
|
57
|
+
}
|
|
58
|
+
}, [currentIndex, imageUrls.length]);
|
|
59
|
+
|
|
60
|
+
const handleZoomIn = useCallback(() => {
|
|
61
|
+
setZoomLevel((prev) => Math.min(prev + 0.25, 3));
|
|
62
|
+
}, []);
|
|
63
|
+
|
|
64
|
+
const handleZoomOut = useCallback(() => {
|
|
65
|
+
setZoomLevel((prev) => Math.max(prev - 0.25, 0.5));
|
|
66
|
+
}, []);
|
|
67
|
+
|
|
68
|
+
const handleResetZoom = useCallback(() => {
|
|
69
|
+
setZoomLevel(1);
|
|
70
|
+
setImagePosition({ x: 0, y: 0 });
|
|
71
|
+
}, []);
|
|
72
|
+
|
|
73
|
+
const handleClose = useCallback(() => {
|
|
74
|
+
setZoomLevel(1);
|
|
75
|
+
setImagePosition({ x: 0, y: 0 });
|
|
76
|
+
onClose();
|
|
77
|
+
}, [onClose]);
|
|
78
|
+
|
|
79
|
+
// Keyboard navigation
|
|
80
|
+
useEffect(() => {
|
|
81
|
+
if (!isOpen) return;
|
|
82
|
+
|
|
83
|
+
const handleKeyDown = (e: KeyboardEvent) => {
|
|
84
|
+
if (e.key === 'ArrowLeft') {
|
|
85
|
+
e.preventDefault();
|
|
86
|
+
if (dir === 'rtl') {
|
|
87
|
+
handleNext();
|
|
88
|
+
} else {
|
|
89
|
+
handlePrevious();
|
|
90
|
+
}
|
|
91
|
+
} else if (e.key === 'ArrowRight') {
|
|
92
|
+
e.preventDefault();
|
|
93
|
+
if (dir === 'rtl') {
|
|
94
|
+
handlePrevious();
|
|
95
|
+
} else {
|
|
96
|
+
handleNext();
|
|
97
|
+
}
|
|
98
|
+
} else if (e.key === 'Escape') {
|
|
99
|
+
e.preventDefault();
|
|
100
|
+
handleClose();
|
|
101
|
+
} else if (e.key === '+' || e.key === '=') {
|
|
102
|
+
e.preventDefault();
|
|
103
|
+
handleZoomIn();
|
|
104
|
+
} else if (e.key === '-') {
|
|
105
|
+
e.preventDefault();
|
|
106
|
+
handleZoomOut();
|
|
107
|
+
} else if (e.key === '0') {
|
|
108
|
+
e.preventDefault();
|
|
109
|
+
handleResetZoom();
|
|
110
|
+
}
|
|
111
|
+
};
|
|
112
|
+
|
|
113
|
+
window.addEventListener('keydown', handleKeyDown);
|
|
114
|
+
return () => window.removeEventListener('keydown', handleKeyDown);
|
|
115
|
+
}, [isOpen, currentIndex, imageUrls.length, dir, handlePrevious, handleNext, handleZoomIn, handleZoomOut, handleResetZoom, handleClose]);
|
|
116
|
+
|
|
117
|
+
// Mouse wheel zoom
|
|
118
|
+
const handleWheel = useCallback(
|
|
119
|
+
(e: React.WheelEvent) => {
|
|
120
|
+
if (e.ctrlKey || e.metaKey) {
|
|
121
|
+
e.preventDefault();
|
|
122
|
+
if (e.deltaY < 0) {
|
|
123
|
+
handleZoomIn();
|
|
124
|
+
} else {
|
|
125
|
+
handleZoomOut();
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
},
|
|
129
|
+
[handleZoomIn, handleZoomOut]
|
|
130
|
+
);
|
|
131
|
+
|
|
132
|
+
// Drag to pan when zoomed
|
|
133
|
+
const handleMouseDown = useCallback(
|
|
134
|
+
(e: React.MouseEvent) => {
|
|
135
|
+
if (zoomLevel > 1) {
|
|
136
|
+
setIsDragging(true);
|
|
137
|
+
setDragStart({ x: e.clientX - imagePosition.x, y: e.clientY - imagePosition.y });
|
|
138
|
+
}
|
|
139
|
+
},
|
|
140
|
+
[zoomLevel, imagePosition]
|
|
141
|
+
);
|
|
142
|
+
|
|
143
|
+
const handleMouseMove = useCallback(
|
|
144
|
+
(e: React.MouseEvent) => {
|
|
145
|
+
if (isDragging && zoomLevel > 1) {
|
|
146
|
+
setImagePosition({
|
|
147
|
+
x: e.clientX - dragStart.x,
|
|
148
|
+
y: e.clientY - dragStart.y,
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
},
|
|
152
|
+
[isDragging, zoomLevel, dragStart]
|
|
153
|
+
);
|
|
154
|
+
|
|
155
|
+
const handleMouseUp = useCallback(() => {
|
|
156
|
+
setIsDragging(false);
|
|
157
|
+
}, []);
|
|
158
|
+
|
|
159
|
+
// Constrain image position when zoomed
|
|
160
|
+
useEffect(() => {
|
|
161
|
+
if (zoomLevel <= 1) {
|
|
162
|
+
setImagePosition({ x: 0, y: 0 });
|
|
163
|
+
}
|
|
164
|
+
}, [zoomLevel]);
|
|
165
|
+
|
|
166
|
+
// Prevent body scroll when dialog is open
|
|
167
|
+
useEffect(() => {
|
|
168
|
+
if (isOpen) {
|
|
169
|
+
document.body.style.overflow = 'hidden';
|
|
170
|
+
} else {
|
|
171
|
+
document.body.style.overflow = 'unset';
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
return () => {
|
|
175
|
+
document.body.style.overflow = 'unset';
|
|
176
|
+
};
|
|
177
|
+
}, [isOpen]);
|
|
178
|
+
|
|
179
|
+
if (!isOpen || !currentImageUrl) return null;
|
|
180
|
+
|
|
181
|
+
return (
|
|
182
|
+
<div
|
|
183
|
+
className='babylai-fixed babylai-inset-0 babylai-z-[9999] babylai-flex babylai-items-center babylai-justify-center'
|
|
184
|
+
onClick={handleClose}
|
|
185
|
+
role='dialog'
|
|
186
|
+
aria-modal='true'
|
|
187
|
+
aria-label='Image preview dialog'
|
|
188
|
+
>
|
|
189
|
+
{/* Backdrop */}
|
|
190
|
+
<div className='babylai-absolute babylai-inset-0 babylai-bg-black babylai-bg-opacity-95' />
|
|
191
|
+
|
|
192
|
+
{/* Dialog content */}
|
|
193
|
+
<div
|
|
194
|
+
ref={containerRef}
|
|
195
|
+
className='babylai-relative babylai-w-full babylai-h-full babylai-flex babylai-items-center babylai-justify-center babylai-overflow-hidden'
|
|
196
|
+
onClick={(e) => e.stopPropagation()}
|
|
197
|
+
onWheel={handleWheel}
|
|
198
|
+
onMouseDown={handleMouseDown}
|
|
199
|
+
onMouseMove={handleMouseMove}
|
|
200
|
+
onMouseUp={handleMouseUp}
|
|
201
|
+
onMouseLeave={handleMouseUp}
|
|
202
|
+
>
|
|
203
|
+
{/* Close Button */}
|
|
204
|
+
<Button
|
|
205
|
+
variant='ghost'
|
|
206
|
+
size='icon'
|
|
207
|
+
onClick={handleClose}
|
|
208
|
+
className={cn(
|
|
209
|
+
'babylai-absolute babylai-top-4 babylai-z-[60]',
|
|
210
|
+
dir === 'rtl' ? 'babylai-left-4' : 'babylai-right-4',
|
|
211
|
+
'babylai-text-white hover:babylai-text-white/80 hover:babylai-bg-white/10',
|
|
212
|
+
'babylai-h-10 babylai-w-10 babylai-rounded-full'
|
|
213
|
+
)}
|
|
214
|
+
aria-label='Close preview'
|
|
215
|
+
type='button'
|
|
216
|
+
>
|
|
217
|
+
<XIcon className='babylai-w-6 babylai-h-6' />
|
|
218
|
+
</Button>
|
|
219
|
+
|
|
220
|
+
{/* Navigation Buttons */}
|
|
221
|
+
{hasMultipleImages && (
|
|
222
|
+
<>
|
|
223
|
+
<Button
|
|
224
|
+
variant='ghost'
|
|
225
|
+
size='icon'
|
|
226
|
+
onClick={dir === 'rtl' ? handleNext : handlePrevious}
|
|
227
|
+
disabled={dir === 'rtl' ? currentIndex === imageUrls.length - 1 : currentIndex === 0}
|
|
228
|
+
className={cn(
|
|
229
|
+
'babylai-absolute babylai-top-1/2 -babylai-translate-y-1/2 babylai-z-[60]',
|
|
230
|
+
dir === 'rtl' ? 'babylai-right-4' : 'babylai-left-4',
|
|
231
|
+
'babylai-text-white hover:babylai-text-white/80 hover:babylai-bg-white/10',
|
|
232
|
+
'babylai-h-12 babylai-w-12 babylai-rounded-full',
|
|
233
|
+
'disabled:babylai-opacity-30'
|
|
234
|
+
)}
|
|
235
|
+
aria-label='Previous image'
|
|
236
|
+
type='button'
|
|
237
|
+
>
|
|
238
|
+
<ArrowRightIcon className={cn('babylai-w-8 babylai-h-8', dir === 'rtl' ? '' : 'babylai-rotate-180')} />
|
|
239
|
+
</Button>
|
|
240
|
+
<Button
|
|
241
|
+
variant='ghost'
|
|
242
|
+
size='icon'
|
|
243
|
+
onClick={dir === 'rtl' ? handlePrevious : handleNext}
|
|
244
|
+
disabled={dir === 'rtl' ? currentIndex === 0 : currentIndex === imageUrls.length - 1}
|
|
245
|
+
className={cn(
|
|
246
|
+
'babylai-absolute babylai-top-1/2 -babylai-translate-y-1/2 babylai-z-[60]',
|
|
247
|
+
dir === 'rtl' ? 'babylai-left-4' : 'babylai-right-4',
|
|
248
|
+
'babylai-text-white hover:babylai-text-white/80 hover:babylai-bg-white/10',
|
|
249
|
+
'babylai-h-12 babylai-w-12 babylai-rounded-full',
|
|
250
|
+
'disabled:babylai-opacity-30'
|
|
251
|
+
)}
|
|
252
|
+
aria-label='Next image'
|
|
253
|
+
type='button'
|
|
254
|
+
>
|
|
255
|
+
<ArrowRightIcon className={cn('babylai-w-8 babylai-h-8', dir === 'rtl' ? 'babylai-rotate-180' : '')} />
|
|
256
|
+
</Button>
|
|
257
|
+
</>
|
|
258
|
+
)}
|
|
259
|
+
|
|
260
|
+
{/* Zoom Controls */}
|
|
261
|
+
<div
|
|
262
|
+
className={cn(
|
|
263
|
+
'babylai-absolute babylai-bottom-4 babylai-z-[60]',
|
|
264
|
+
'babylai-flex babylai-items-center babylai-gap-2',
|
|
265
|
+
'babylai-bg-black/50 babylai-backdrop-blur-sm babylai-rounded-lg babylai-p-2',
|
|
266
|
+
dir === 'rtl' ? 'babylai-right-1/2 babylai-translate-x-1/2' : 'babylai-left-1/2 -babylai-translate-x-1/2'
|
|
267
|
+
)}
|
|
268
|
+
>
|
|
269
|
+
<Button
|
|
270
|
+
variant='ghost'
|
|
271
|
+
size='icon'
|
|
272
|
+
onClick={handleZoomOut}
|
|
273
|
+
disabled={zoomLevel <= 0.5}
|
|
274
|
+
className='babylai-text-white hover:babylai-text-white/80 hover:babylai-bg-white/10 babylai-h-9 babylai-w-9 disabled:babylai-opacity-30'
|
|
275
|
+
aria-label='Zoom out'
|
|
276
|
+
type='button'
|
|
277
|
+
>
|
|
278
|
+
<svg className='babylai-w-5 babylai-h-5' fill='none' stroke='currentColor' viewBox='0 0 24 24'>
|
|
279
|
+
<path strokeLinecap='round' strokeLinejoin='round' strokeWidth={2} d='M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0zM13 10H7' />
|
|
280
|
+
</svg>
|
|
281
|
+
</Button>
|
|
282
|
+
<span className='babylai-text-white babylai-text-sm babylai-font-medium babylai-min-w-12 babylai-text-center'>
|
|
283
|
+
{Math.round(zoomLevel * 100)}%
|
|
284
|
+
</span>
|
|
285
|
+
<Button
|
|
286
|
+
variant='ghost'
|
|
287
|
+
size='icon'
|
|
288
|
+
onClick={handleZoomIn}
|
|
289
|
+
disabled={zoomLevel >= 3}
|
|
290
|
+
className='babylai-text-white hover:babylai-text-white/80 hover:babylai-bg-white/10 babylai-h-9 babylai-w-9 disabled:babylai-opacity-30'
|
|
291
|
+
aria-label='Zoom in'
|
|
292
|
+
type='button'
|
|
293
|
+
>
|
|
294
|
+
<svg className='babylai-w-5 babylai-h-5' fill='none' stroke='currentColor' viewBox='0 0 24 24'>
|
|
295
|
+
<path strokeLinecap='round' strokeLinejoin='round' strokeWidth={2} d='M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0zM10 7v6m3-3H7' />
|
|
296
|
+
</svg>
|
|
297
|
+
</Button>
|
|
298
|
+
{zoomLevel !== 1 && (
|
|
299
|
+
<Button
|
|
300
|
+
variant='ghost'
|
|
301
|
+
size='sm'
|
|
302
|
+
onClick={handleResetZoom}
|
|
303
|
+
className='babylai-text-white hover:babylai-text-white/80 hover:babylai-bg-white/10 babylai-h-9 babylai-px-3 babylai-ml-2'
|
|
304
|
+
aria-label='Reset zoom'
|
|
305
|
+
type='button'
|
|
306
|
+
>
|
|
307
|
+
Reset
|
|
308
|
+
</Button>
|
|
309
|
+
)}
|
|
310
|
+
</div>
|
|
311
|
+
|
|
312
|
+
{/* Image Counter */}
|
|
313
|
+
{hasMultipleImages && (
|
|
314
|
+
<div
|
|
315
|
+
className={cn(
|
|
316
|
+
'babylai-absolute babylai-top-4 babylai-z-[60]',
|
|
317
|
+
'babylai-bg-black/50 babylai-backdrop-blur-sm babylai-rounded-lg babylai-px-4 babylai-py-2',
|
|
318
|
+
dir === 'rtl' ? 'babylai-right-1/2 babylai-translate-x-1/2' : 'babylai-left-1/2 -babylai-translate-x-1/2'
|
|
319
|
+
)}
|
|
320
|
+
>
|
|
321
|
+
<span className='babylai-text-white babylai-text-sm babylai-font-medium'>
|
|
322
|
+
{currentIndex + 1} / {imageUrls.length}
|
|
323
|
+
</span>
|
|
324
|
+
</div>
|
|
325
|
+
)}
|
|
326
|
+
|
|
327
|
+
{/* Image Container */}
|
|
328
|
+
<div
|
|
329
|
+
className='babylai-flex babylai-items-center babylai-justify-center'
|
|
330
|
+
style={{
|
|
331
|
+
transform: `translate(${imagePosition.x}px, ${imagePosition.y}px)`,
|
|
332
|
+
cursor: zoomLevel > 1 ? (isDragging ? 'grabbing' : 'grab') : 'default',
|
|
333
|
+
}}
|
|
334
|
+
>
|
|
335
|
+
<img
|
|
336
|
+
ref={imageRef}
|
|
337
|
+
src={currentImageUrl}
|
|
338
|
+
alt={alt}
|
|
339
|
+
className='babylai-max-w-[90vw] babylai-max-h-[85vh] babylai-object-contain babylai-select-none'
|
|
340
|
+
style={{
|
|
341
|
+
transform: `scale(${zoomLevel})`,
|
|
342
|
+
transformOrigin: 'center center',
|
|
343
|
+
}}
|
|
344
|
+
draggable={false}
|
|
345
|
+
/>
|
|
346
|
+
</div>
|
|
347
|
+
</div>
|
|
348
|
+
</div>
|
|
349
|
+
);
|
|
350
|
+
};
|
|
351
|
+
|
|
352
|
+
ImagePreviewDialog.displayName = 'ImagePreviewDialog';
|
|
353
|
+
|
|
354
|
+
export default ImagePreviewDialog;
|
package/src/core/AblyService.ts
CHANGED
|
@@ -81,12 +81,15 @@ export class ClientAblyService {
|
|
|
81
81
|
const messageContent =
|
|
82
82
|
typeof message.data === 'string' ? message.data : message.data?.content || message.data?.message;
|
|
83
83
|
const senderType = message.data?.senderType || 3; // Assistant
|
|
84
|
-
const needsAgent =
|
|
84
|
+
const needsAgent =
|
|
85
|
+
message.data?.needsAgent ||
|
|
86
|
+
message.data?.actionType == "needs_agent" ||
|
|
87
|
+
false;
|
|
88
|
+
const attachments = message.data?.attachments || [];
|
|
85
89
|
|
|
86
|
-
onMessageReceived(messageContent, senderType, needsAgent);
|
|
90
|
+
onMessageReceived(messageContent, senderType, needsAgent, attachments);
|
|
87
91
|
} catch (error) {
|
|
88
92
|
// Handle error silently
|
|
89
|
-
console.error('[AblyService] Error processing ReceiveMessage:', error);
|
|
90
93
|
}
|
|
91
94
|
});
|
|
92
95
|
|
package/src/core/ApiService.ts
CHANGED
|
@@ -61,7 +61,6 @@ export class ApiService {
|
|
|
61
61
|
this.tokenExpiryTime = currentTime + response.expiresIn;
|
|
62
62
|
return this.currentToken;
|
|
63
63
|
} catch (error) {
|
|
64
|
-
console.error('Error getting token:', error);
|
|
65
64
|
throw error;
|
|
66
65
|
}
|
|
67
66
|
}
|
|
@@ -113,6 +112,5 @@ export class ApiService {
|
|
|
113
112
|
if (this.config.onError) {
|
|
114
113
|
this.config.onError(error instanceof Error ? error : new Error('Unknown error occurred'));
|
|
115
114
|
}
|
|
116
|
-
console.error('API Error:', error);
|
|
117
115
|
}
|
|
118
116
|
}
|