@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.
@@ -1,9 +1,10 @@
1
+ import React from 'react';
1
2
  interface ChatWindowFooterProps {
2
3
  inputMessage: string;
3
4
  setInputMessage: (e: string) => void;
4
- handleKeyDown: (e: React.KeyboardEvent) => void;
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 {};
@@ -1,4 +1,4 @@
1
- import "../globals.css";
1
+ import '../globals.css';
2
2
  interface HelpCenterProps {
3
3
  helpScreenId: string;
4
4
  language: string;
@@ -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
@@ -3,7 +3,7 @@
3
3
  "main": "dist/index.js",
4
4
  "module": "dist/index.esm.js",
5
5
  "types": "dist/index.d.ts",
6
- "version": "3.0.21",
6
+ "version": "3.2.1",
7
7
  "description": "BabylAI Help Center Widget for React and Next.js",
8
8
  "private": false,
9
9
  "exports": {
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,4 @@
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
+ <line x1="18" y1="6" x2="6" y2="18"/>
3
+ <line x1="6" y1="6" x2="18" y2="18"/>
4
+ </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;
@@ -0,0 +1,4 @@
1
+ export { ImageAttachment } from './image-attachment';
2
+ export { ImagePreviewDialog } from './image-preview-dialog';
3
+ export { default as AgentResponse } from './agent-response/agent-response';
4
+ export { default as Header } from './header';
@@ -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 = message.data?.needsAgent || false;
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
 
@@ -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
  }