@aslaluroba/help-center-react 3.2.5 → 3.2.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (97) hide show
  1. package/README.md +0 -3
  2. package/dist/components/shared/Button/button.d.ts +3 -3
  3. package/dist/components/ui/index.d.ts +0 -1
  4. package/dist/core/api.d.ts +4 -4
  5. package/dist/index.css +1 -1
  6. package/dist/index.esm.js +15543 -15136
  7. package/dist/index.esm.js.map +1 -1
  8. package/dist/index.js +15546 -15145
  9. package/dist/index.js.map +1 -1
  10. package/dist/lib/theme-utils.d.ts +0 -9
  11. package/dist/lib/types.d.ts +6 -3
  12. package/dist/services.esm.js +9474 -9222
  13. package/dist/services.esm.js.map +1 -1
  14. package/dist/services.js +9472 -9222
  15. package/dist/services.js.map +1 -1
  16. package/dist/ui/chatbot-popup/active-chat-actions.d.ts +7 -0
  17. package/dist/ui/chatbot-popup/chat-window-screen/action-button.d.ts +10 -0
  18. package/dist/ui/chatbot-popup/chat-window-screen/footer.d.ts +1 -0
  19. package/dist/ui/chatbot-popup/chat-window-screen/header.d.ts +2 -5
  20. package/dist/ui/chatbot-popup/chat-window-screen/in-chat-review.d.ts +9 -0
  21. package/dist/ui/chatbot-popup/chat-window-screen/index.d.ts +7 -3
  22. package/dist/ui/chatbot-popup/chat-window-screen/typing-indicator.d.ts +6 -0
  23. package/dist/ui/chatbot-popup/error-screen/index.d.ts +0 -1
  24. package/dist/ui/chatbot-popup/loading-screen/index.d.ts +0 -2
  25. package/dist/ui/chatbot-popup/options-list-screen/company-card.d.ts +9 -0
  26. package/dist/ui/chatbot-popup/options-list-screen/header.d.ts +1 -2
  27. package/dist/ui/chatbot-popup/options-list-screen/helpscreen-intro.d.ts +6 -0
  28. package/dist/ui/chatbot-popup/options-list-screen/helpscreen-list.d.ts +10 -0
  29. package/dist/ui/chatbot-popup/options-list-screen/helpscreen-option.d.ts +9 -0
  30. package/dist/ui/chatbot-popup/options-list-screen/index.d.ts +1 -4
  31. package/dist/ui/help-center.d.ts +2 -5
  32. package/dist/ui/help-popup.d.ts +16 -7
  33. package/dist/ui/review-dialog/index.d.ts +2 -1
  34. package/package.json +35 -29
  35. package/src/components/shared/Button/button.tsx +11 -18
  36. package/src/components/shared/Card/card.tsx +8 -8
  37. package/src/components/ui/agent-response/agent-response.tsx +4 -4
  38. package/src/components/ui/image-attachment.tsx +8 -8
  39. package/src/components/ui/image-preview-dialog.tsx +41 -41
  40. package/src/components/ui/index.ts +0 -1
  41. package/src/core/AblyService.ts +22 -17
  42. package/src/core/api.ts +9 -7
  43. package/src/globals.css +216 -50
  44. package/src/lib/theme-utils.ts +1 -33
  45. package/src/lib/types.ts +7 -4
  46. package/src/locales/ar.json +16 -6
  47. package/src/locales/en.json +16 -6
  48. package/src/types/icons.d.ts +6 -0
  49. package/src/ui/chatbot-popup/active-chat-actions.tsx +39 -0
  50. package/src/ui/chatbot-popup/chat-window-screen/action-button.tsx +37 -0
  51. package/src/ui/chatbot-popup/chat-window-screen/footer.tsx +42 -43
  52. package/src/ui/chatbot-popup/chat-window-screen/header.tsx +34 -67
  53. package/src/ui/chatbot-popup/chat-window-screen/in-chat-review.tsx +83 -0
  54. package/src/ui/chatbot-popup/chat-window-screen/index.tsx +49 -42
  55. package/src/ui/chatbot-popup/chat-window-screen/typing-indicator.tsx +27 -0
  56. package/src/ui/chatbot-popup/error-screen/index.tsx +7 -7
  57. package/src/ui/chatbot-popup/loading-screen/index.tsx +6 -17
  58. package/src/ui/chatbot-popup/options-list-screen/company-card.tsx +37 -0
  59. package/src/ui/chatbot-popup/options-list-screen/header.tsx +12 -31
  60. package/src/ui/chatbot-popup/options-list-screen/helpscreen-intro.tsx +32 -0
  61. package/src/ui/chatbot-popup/options-list-screen/helpscreen-list.tsx +48 -0
  62. package/src/ui/chatbot-popup/options-list-screen/helpscreen-option.tsx +38 -0
  63. package/src/ui/chatbot-popup/options-list-screen/index.tsx +44 -38
  64. package/src/ui/confirmation-modal/index.tsx +27 -12
  65. package/src/ui/floating-message.tsx +8 -7
  66. package/src/ui/help-button.tsx +5 -5
  67. package/src/ui/help-center.tsx +71 -59
  68. package/src/ui/help-popup.tsx +114 -91
  69. package/src/ui/powered-by.tsx +49 -7
  70. package/src/ui/review-dialog/index.tsx +48 -65
  71. package/src/ui/review-dialog/rating.tsx +7 -7
  72. package/dist/components/ui/header.d.ts +0 -6
  73. package/dist/ui/chatbot-popup/home-screen/card.d.ts +0 -6
  74. package/dist/ui/chatbot-popup/home-screen/chat-now-card.d.ts +0 -5
  75. package/dist/ui/chatbot-popup/home-screen/index.d.ts +0 -7
  76. package/dist/ui/chatbot-popup/options-list-screen/expanded-option.d.ts +0 -7
  77. package/dist/ui/chatbot-popup/options-list-screen/option-card.d.ts +0 -5
  78. package/src/assets/icons/arrowRight.svg +0 -3
  79. package/src/assets/icons/chat.svg +0 -4
  80. package/src/assets/icons/close.svg +0 -1
  81. package/src/assets/icons/closeCircle.svg +0 -3
  82. package/src/assets/icons/closeCirclePrimary.svg +0 -4
  83. package/src/assets/icons/envelope.svg +0 -3
  84. package/src/assets/icons/paperclip.svg +0 -3
  85. package/src/assets/icons/threeDots.svg +0 -3
  86. package/src/assets/icons/user.svg +0 -3
  87. package/src/assets/icons/x.svg +0 -4
  88. package/src/assets/logoColors.svg +0 -5
  89. package/src/assets/logo_ai.svg +0 -14
  90. package/src/assets/thinking-logo.svg +0 -3
  91. package/src/components/ui/header.tsx +0 -22
  92. package/src/ui/chatbot-popup/home-screen/card.tsx +0 -33
  93. package/src/ui/chatbot-popup/home-screen/chat-now-card.tsx +0 -36
  94. package/src/ui/chatbot-popup/home-screen/index.tsx +0 -44
  95. package/src/ui/chatbot-popup/options-list-screen/expanded-option.tsx +0 -37
  96. package/src/ui/chatbot-popup/options-list-screen/option-card.tsx +0 -31
  97. /package/src/assets/{icons/seperator.svg → seperator.svg} +0 -0
@@ -1,8 +1,8 @@
1
1
  import React, { useEffect, useCallback, useRef, useState } from 'react';
2
2
  import { cn } from '@/lib/utils';
3
3
  import { Button } from '@/components';
4
- import XIcon from '../../assets/icons/x.svg';
5
- import ArrowRightIcon from '../../assets/icons/arrowRight.svg';
4
+ import MaterialSymbolsCloseSmallOutlineRounded from '~icons/material-symbols/close-small-outline-rounded'
5
+ import SolarArrowRightBoldDuotone from '~icons/solar/arrow-right-bold-duotone';
6
6
  import { useLocalTranslation } from '../../useLocalTranslation';
7
7
 
8
8
  interface ImagePreviewDialogProps {
@@ -212,19 +212,19 @@ export const ImagePreviewDialog: React.FC<ImagePreviewDialogProps> = ({
212
212
 
213
213
  return (
214
214
  <div
215
- className='babylai-fixed babylai-inset-0 babylai-z-[9999] babylai-flex babylai-items-center babylai-justify-center'
215
+ className='babylai:fixed babylai:inset-0 babylai:z-9999 babylai:flex babylai:items-center babylai:justify-center'
216
216
  onClick={handleClose}
217
217
  role='dialog'
218
218
  aria-modal='true'
219
219
  aria-label='Image preview dialog'
220
220
  >
221
221
  {/* Backdrop */}
222
- <div className='babylai-absolute babylai-inset-0 babylai-bg-black babylai-bg-opacity-95' />
222
+ <div className='babylai:absolute babylai:inset-0 babylai:bg-black babylai:bg-opacity-95' />
223
223
 
224
224
  {/* Dialog content */}
225
225
  <div
226
226
  ref={containerRef}
227
- className='babylai-relative babylai-w-full babylai-h-full babylai-flex babylai-items-center babylai-justify-center babylai-overflow-hidden'
227
+ className='babylai:relative babylai:w-full babylai:h-full babylai:flex babylai:items-center babylai:justify-center babylai:overflow-hidden'
228
228
  onClick={(e) => e.stopPropagation()}
229
229
  onWheel={handleWheel}
230
230
  onMouseDown={handleMouseDown}
@@ -238,15 +238,15 @@ export const ImagePreviewDialog: React.FC<ImagePreviewDialogProps> = ({
238
238
  size='icon'
239
239
  onClick={handleClose}
240
240
  className={cn(
241
- 'babylai-absolute babylai-top-4 babylai-z-[60]',
242
- dir === 'rtl' ? 'babylai-left-4' : 'babylai-right-4',
243
- 'babylai-text-white hover:babylai-text-white/80 hover:babylai-bg-white/10',
244
- 'babylai-h-10 babylai-w-10 babylai-rounded-full'
241
+ 'babylai:absolute babylai:top-4 babylai:z-60',
242
+ dir === 'rtl' ? 'babylai:left-4' : 'babylai:right-4',
243
+ 'babylai:text-white babylai:hover:text-white/80 babylai:hover:bg-white/10',
244
+ 'babylai:h-10 babylai:w-10 babylai:rounded-full'
245
245
  )}
246
246
  aria-label='Close preview'
247
247
  type='button'
248
248
  >
249
- <XIcon className='babylai-w-6 babylai-h-6' />
249
+ <MaterialSymbolsCloseSmallOutlineRounded className='babylai:w-6 babylai:h-6' />
250
250
  </Button>
251
251
 
252
252
  {/* Navigation Buttons */}
@@ -258,16 +258,16 @@ export const ImagePreviewDialog: React.FC<ImagePreviewDialogProps> = ({
258
258
  onClick={dir === 'rtl' ? handleNext : handlePrevious}
259
259
  disabled={dir === 'rtl' ? currentIndex === imageUrls.length - 1 : currentIndex === 0}
260
260
  className={cn(
261
- 'babylai-absolute babylai-top-1/2 -babylai-translate-y-1/2 babylai-z-[60]',
262
- dir === 'rtl' ? 'babylai-right-4' : 'babylai-left-4',
263
- 'babylai-text-white hover:babylai-text-white/80 hover:babylai-bg-white/10',
264
- 'babylai-h-12 babylai-w-12 babylai-rounded-full',
265
- 'disabled:babylai-opacity-30'
261
+ 'babylai:absolute babylai:top-1/2 babylai:-translate-y-1/2 babylai:z-60',
262
+ dir === 'rtl' ? 'babylai:right-4' : 'babylai:left-4',
263
+ 'babylai:text-white babylai:hover:text-white/80 babylai:hover:bg-white/10',
264
+ 'babylai:h-12 babylai:w-12 babylai:rounded-full',
265
+ 'babylai:disabled:opacity-30'
266
266
  )}
267
267
  aria-label='Previous image'
268
268
  type='button'
269
269
  >
270
- <ArrowRightIcon className={cn('babylai-w-8 babylai-h-8', dir === 'rtl' ? '' : 'babylai-rotate-180')} />
270
+ <SolarArrowRightBoldDuotone className={cn('babylai:w-8 babylai:h-8', dir === 'rtl' ? '' : 'babylai:rotate-180')} />
271
271
  </Button>
272
272
  <Button
273
273
  variant='ghost'
@@ -275,16 +275,16 @@ export const ImagePreviewDialog: React.FC<ImagePreviewDialogProps> = ({
275
275
  onClick={dir === 'rtl' ? handlePrevious : handleNext}
276
276
  disabled={dir === 'rtl' ? currentIndex === 0 : currentIndex === imageUrls.length - 1}
277
277
  className={cn(
278
- 'babylai-absolute babylai-top-1/2 -babylai-translate-y-1/2 babylai-z-[60]',
279
- dir === 'rtl' ? 'babylai-left-4' : 'babylai-right-4',
280
- 'babylai-text-white hover:babylai-text-white/80 hover:babylai-bg-white/10',
281
- 'babylai-h-12 babylai-w-12 babylai-rounded-full',
282
- 'disabled:babylai-opacity-30'
278
+ 'babylai:absolute babylai:top-1/2 babylai:-translate-y-1/2 babylai:z-60',
279
+ dir === 'rtl' ? 'babylai:left-4' : 'babylai:right-4',
280
+ 'babylai:text-white babylai:hover:text-white/80 babylai:hover:bg-white/10',
281
+ 'babylai:h-12 babylai:w-12 babylai:rounded-full',
282
+ 'babylai:disabled:opacity-30'
283
283
  )}
284
284
  aria-label='Next image'
285
285
  type='button'
286
286
  >
287
- <ArrowRightIcon className={cn('babylai-w-8 babylai-h-8', dir === 'rtl' ? 'babylai-rotate-180' : '')} />
287
+ <SolarArrowRightBoldDuotone className={cn('babylai:w-8 babylai:h-8', dir === 'rtl' ? 'babylai:rotate-180' : '')} />
288
288
  </Button>
289
289
  </>
290
290
  )}
@@ -292,10 +292,10 @@ export const ImagePreviewDialog: React.FC<ImagePreviewDialogProps> = ({
292
292
  {/* Zoom Controls */}
293
293
  <div
294
294
  className={cn(
295
- 'babylai-absolute babylai-bottom-4 babylai-z-[60]',
296
- 'babylai-flex babylai-items-center babylai-gap-2',
297
- 'babylai-bg-black/50 babylai-backdrop-blur-sm babylai-rounded-lg babylai-p-2',
298
- dir === 'rtl' ? 'babylai-right-1/2 babylai-translate-x-1/2' : 'babylai-left-1/2 -babylai-translate-x-1/2'
295
+ 'babylai:absolute babylai:bottom-4 babylai:z-60',
296
+ 'babylai:flex babylai:items-center babylai:gap-2',
297
+ 'babylai:bg-black/50 babylai:backdrop-blur-sm babylai:rounded-lg babylai:p-2',
298
+ dir === 'rtl' ? 'babylai:right-1/2 babylai:translate-x-1/2' : 'babylai:left-1/2 babylai:-translate-x-1/2'
299
299
  )}
300
300
  >
301
301
  <Button
@@ -303,15 +303,15 @@ export const ImagePreviewDialog: React.FC<ImagePreviewDialogProps> = ({
303
303
  size='icon'
304
304
  onClick={handleZoomOut}
305
305
  disabled={zoomLevel <= 0.5}
306
- className='babylai-text-white hover:babylai-text-white/80 hover:babylai-bg-white/10 babylai-h-9 babylai-w-9 disabled:babylai-opacity-30'
306
+ className='babylai:text-white babylai:hover:text-white/80 babylai:hover:bg-white/10 babylai:h-9 babylai:w-9 babylai:disabled:opacity-30'
307
307
  aria-label='Zoom out'
308
308
  type='button'
309
309
  >
310
- <svg className='babylai-w-5 babylai-h-5' fill='none' stroke='currentColor' viewBox='0 0 24 24'>
310
+ <svg className='babylai:w-5 babylai:h-5' fill='none' stroke='currentColor' viewBox='0 0 24 24'>
311
311
  <path strokeLinecap='round' strokeLinejoin='round' strokeWidth={2} d='M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0zM13 10H7' />
312
312
  </svg>
313
313
  </Button>
314
- <span className='babylai-text-white babylai-text-sm babylai-font-medium babylai-min-w-12 babylai-text-center'>
314
+ <span className='babylai:text-white babylai:text-sm babylai:font-medium babylai:min-w-12 babylai:text-center'>
315
315
  {Math.round(zoomLevel * 100)}%
316
316
  </span>
317
317
  <Button
@@ -319,11 +319,11 @@ export const ImagePreviewDialog: React.FC<ImagePreviewDialogProps> = ({
319
319
  size='icon'
320
320
  onClick={handleZoomIn}
321
321
  disabled={zoomLevel >= 3}
322
- className='babylai-text-white hover:babylai-text-white/80 hover:babylai-bg-white/10 babylai-h-9 babylai-w-9 disabled:babylai-opacity-30'
322
+ className='babylai:text-white babylai:hover:text-white/80 babylai:hover:bg-white/10 babylai:h-9 babylai:w-9 babylai:disabled:opacity-30'
323
323
  aria-label='Zoom in'
324
324
  type='button'
325
325
  >
326
- <svg className='babylai-w-5 babylai-h-5' fill='none' stroke='currentColor' viewBox='0 0 24 24'>
326
+ <svg className='babylai:w-5 babylai:h-5' fill='none' stroke='currentColor' viewBox='0 0 24 24'>
327
327
  <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' />
328
328
  </svg>
329
329
  </Button>
@@ -332,7 +332,7 @@ export const ImagePreviewDialog: React.FC<ImagePreviewDialogProps> = ({
332
332
  variant='ghost'
333
333
  size='sm'
334
334
  onClick={handleResetZoom}
335
- className='babylai-text-white hover:babylai-text-white/80 hover:babylai-bg-white/10 babylai-h-9 babylai-px-3 babylai-ml-2'
335
+ className='babylai:text-white babylai:hover:text-white/80 babylai:hover:bg-white/10 babylai:h-9 babylai:px-3 babylai:ml-2'
336
336
  aria-label='Reset zoom'
337
337
  type='button'
338
338
  >
@@ -340,16 +340,16 @@ export const ImagePreviewDialog: React.FC<ImagePreviewDialogProps> = ({
340
340
  </Button>
341
341
  )}
342
342
  {/* Download Button */}
343
- <div className='babylai-h-9 babylai-w-px babylai-bg-white/20 babylai-mx-1' />
343
+ <div className='babylai:h-9 babylai:w-px babylai:bg-white/20 babylai:mx-1' />
344
344
  <Button
345
345
  variant='ghost'
346
346
  size='icon'
347
347
  onClick={handleDownload}
348
- className='babylai-text-white hover:babylai-text-white/80 hover:babylai-bg-white/10 babylai-h-9 babylai-w-9'
348
+ className='babylai:text-white babylai:hover:text-white/80 babylai:hover:bg-white/10 babylai:h-9 babylai:w-9'
349
349
  aria-label='Download image'
350
350
  type='button'
351
351
  >
352
- <svg className='babylai-w-5 babylai-h-5' fill='none' stroke='currentColor' viewBox='0 0 24 24'>
352
+ <svg className='babylai:w-5 babylai:h-5' fill='none' stroke='currentColor' viewBox='0 0 24 24'>
353
353
  <path strokeLinecap='round' strokeLinejoin='round' strokeWidth={2} d='M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-4l-4 4m0 0l-4-4m4 4V4' />
354
354
  </svg>
355
355
  </Button>
@@ -359,12 +359,12 @@ export const ImagePreviewDialog: React.FC<ImagePreviewDialogProps> = ({
359
359
  {hasMultipleImages && (
360
360
  <div
361
361
  className={cn(
362
- 'babylai-absolute babylai-top-4 babylai-z-[60]',
363
- 'babylai-bg-black/50 babylai-backdrop-blur-sm babylai-rounded-lg babylai-px-4 babylai-py-2',
364
- dir === 'rtl' ? 'babylai-right-1/2 babylai-translate-x-1/2' : 'babylai-left-1/2 -babylai-translate-x-1/2'
362
+ 'babylai:absolute babylai:top-4 babylai:z-60',
363
+ 'babylai:bg-black/50 babylai:backdrop-blur-sm babylai:rounded-lg babylai:px-4 babylai:py-2',
364
+ dir === 'rtl' ? 'babylai:right-1/2 babylai:translate-x-1/2' : 'babylai:left-1/2 babylai:-translate-x-1/2'
365
365
  )}
366
366
  >
367
- <span className='babylai-text-white babylai-text-sm babylai-font-medium'>
367
+ <span className='babylai:text-white babylai:text-sm babylai:font-medium'>
368
368
  {currentIndex + 1} / {imageUrls.length}
369
369
  </span>
370
370
  </div>
@@ -372,7 +372,7 @@ export const ImagePreviewDialog: React.FC<ImagePreviewDialogProps> = ({
372
372
 
373
373
  {/* Image Container */}
374
374
  <div
375
- className='babylai-flex babylai-items-center babylai-justify-center'
375
+ className='babylai:flex babylai:items-center babylai:justify-center'
376
376
  style={{
377
377
  transform: `translate(${imagePosition.x}px, ${imagePosition.y}px)`,
378
378
  cursor: zoomLevel > 1 ? (isDragging ? 'grabbing' : 'grab') : 'default',
@@ -382,7 +382,7 @@ export const ImagePreviewDialog: React.FC<ImagePreviewDialogProps> = ({
382
382
  ref={imageRef}
383
383
  src={currentImageUrl}
384
384
  alt={alt}
385
- className='babylai-max-w-[90vw] babylai-max-h-[85vh] babylai-object-contain babylai-select-none'
385
+ className='babylai:max-w-[90vw] babylai:max-h-[85vh] babylai:object-contain babylai:select-none'
386
386
  style={{
387
387
  transform: `scale(${zoomLevel})`,
388
388
  transformOrigin: 'center center',
@@ -1,4 +1,3 @@
1
1
  export { ImageAttachment } from './image-attachment';
2
2
  export { ImagePreviewDialog } from './image-preview-dialog';
3
3
  export { default as AgentResponse } from './agent-response/agent-response';
4
- export { default as Header } from './header';
@@ -133,23 +133,28 @@ export class ClientAblyService {
133
133
  | null;
134
134
 
135
135
  // Extract downloadUrl from attachments (Ably now returns downloadUrl directly)
136
- // Attachments can be: strings (URLs), objects with downloadUrl, or objects with id
137
- const attachmentUrls = attachments
138
- .map((attachment: any) => {
139
- if (typeof attachment === 'string') {
140
- // If it's already a string, it's a URL
141
- return attachment;
142
- } else if (attachment?.downloadUrl) {
143
- // If it's an object with downloadUrl, use that
144
- return attachment.downloadUrl;
145
- } else if (attachment?.url) {
146
- // Fallback to url property
147
- return attachment.url;
136
+ // Attachments can be: strings (URLs), objects with downloadUrl, url, or id only
137
+ const attachmentUrls: string[] = [];
138
+ const attachmentIdsFromAttachments: string[] = [];
139
+ for (const attachment of attachments) {
140
+ if (typeof attachment === 'string') {
141
+ if (attachment.startsWith('http://') || attachment.startsWith('https://')) {
142
+ attachmentUrls.push(attachment);
143
+ } else {
144
+ attachmentIdsFromAttachments.push(attachment);
148
145
  }
149
- // If it's an object with id, we'll need to keep it for backward compatibility
150
- return null;
151
- })
152
- .filter((url: string | null): url is string => url !== null);
146
+ } else if (attachment?.downloadUrl) {
147
+ attachmentUrls.push(attachment.downloadUrl);
148
+ } else if (attachment?.url) {
149
+ attachmentUrls.push(attachment.url);
150
+ } else if (attachment?.id && typeof attachment.id === 'string') {
151
+ attachmentIdsFromAttachments.push(attachment.id);
152
+ }
153
+ }
154
+ const attachmentIds =
155
+ attachmentIdsFromAttachments.length > 0
156
+ ? attachmentIdsFromAttachments
157
+ : (rawData?.attachmentIds as string[] | undefined) || [];
153
158
 
154
159
  // Invoke optional action handler first (non-blocking for message processing)
155
160
  if (this.onActionReceived && actionType !== undefined) {
@@ -164,7 +169,7 @@ export class ClientAblyService {
164
169
  }
165
170
  }
166
171
 
167
- onMessageReceived(messageContent, senderType, needsAgent, attachmentUrls);
172
+ onMessageReceived(messageContent, senderType, needsAgent, attachmentUrls, attachmentIds);
168
173
  } catch (error) {
169
174
  console.error('[AblyService] Error processing message', { error, message });
170
175
  }
package/src/core/api.ts CHANGED
@@ -1,4 +1,5 @@
1
- import { TokenResponse, PresignUploadRequestDto, PresignUploadResponse, PresignDownloadResponse } from '../lib/types';
1
+ import { defaultLanguage } from '@/i18n';
2
+ import { TokenResponse, PresignUploadRequestDto, PresignUploadResponse, PresignDownloadResponse } from '@/lib/types';
2
3
 
3
4
  let getTokenFunction: (() => Promise<TokenResponse>) | undefined = undefined;
4
5
  let baseUrl: string | null = null;
@@ -136,16 +137,17 @@ export async function apiRequest(
136
137
  endpoint: string,
137
138
  method = 'GET',
138
139
  body: any = null,
139
- options: { cache?: boolean; timeout?: number; language: string }
140
+ options: { cache?: boolean; timeout?: number; language?: 'ar' | 'en' }
140
141
  ) {
141
142
  if (!baseUrl) throw new Error('API not initialized');
142
143
 
143
144
  const url = `${baseUrl}/${endpoint}`;
144
145
  const requestKey = `${method}:${endpoint}:${JSON.stringify(body)}`;
145
146
 
146
- // Check for duplicate in-flight requests
147
+ // Check for duplicate in-flight requests (skip for GET: callers receive Response and call .json();
148
+ // sharing the same Response causes "body already consumed" when the second caller parses)
147
149
  const duplicateRequest = getDuplicateRequest(requestKey);
148
- if (duplicateRequest) {
150
+ if (duplicateRequest && method !== 'GET') {
149
151
  return duplicateRequest;
150
152
  }
151
153
 
@@ -162,7 +164,7 @@ export async function apiRequest(
162
164
  headers: {
163
165
  'Content-Type': 'application/json',
164
166
  'Cache-Control': method === 'GET' ? 'max-age=30' : 'no-cache',
165
- 'Accept-Language': options.language,
167
+ 'Accept-Language': options.language || defaultLanguage,
166
168
  },
167
169
  body: body ? JSON.stringify(body) : null,
168
170
  };
@@ -218,7 +220,7 @@ export async function apiRequest(
218
220
  export async function presignUpload(
219
221
  chatSessionId: string,
220
222
  file: File,
221
- language: string
223
+ language: 'ar' | 'en' = defaultLanguage
222
224
  ): Promise<PresignUploadResponse> {
223
225
  const requestBody: PresignUploadRequestDto = {
224
226
  name: file.name,
@@ -238,7 +240,7 @@ export async function presignUpload(
238
240
 
239
241
  return await response.json();
240
242
  }
241
- export async function presignDownload(fileId: string, language: string): Promise<PresignDownloadResponse> {
243
+ export async function presignDownload(fileId: string, language: 'ar' | 'en' = defaultLanguage): Promise<PresignDownloadResponse> {
242
244
  try {
243
245
  const response = await apiRequest(`NewFile/${fileId}/presign-download`, 'GET', null, {
244
246
  language,
package/src/globals.css CHANGED
@@ -1,68 +1,234 @@
1
- /* @tailwind base; */
2
- @tailwind components;
3
- @tailwind utilities;
4
-
5
- :root,
6
- :host {
7
- --babylai-primary-color: #AD49E1;
8
- --babylai-secondary-color: #F6ECFC;
9
- --babylai-storm-dust: #3d3d3d;
10
- --babylai-storm-dust-950: #262626;
11
- }
1
+ @import url('https://fonts.googleapis.com/css2?family=Cairo:wght@200..1000&display=swap');
2
+ @import "tailwindcss" prefix(babylai);
12
3
 
13
- *,
14
- :before,
15
- :after {
16
- --tw-gradient-from-position: ;
17
- --tw-gradient-via-position: ;
18
- --tw-gradient-to-position: ;
19
- box-sizing: border-box;
20
- }
4
+ /* CSS Custom Properties - Light Theme (Default) */
5
+ :root {
6
+ --babylai-font-sans: "Cairo", sans-serif;
7
+ /* Light theme colors */
8
+ --black-white-50: #ffffff;
9
+ --black-white-100: #f3f3f3;
10
+ --black-white-200: #e2e2e2;
11
+ --black-white-300: #919191;
12
+ --black-white-400: #606060;
13
+ --black-white-500: #333333;
14
+ --black-white-600: #1f1f1f;
15
+ --black-white-700: #171717;
16
+ --black-white-800: #0a0a0a;
17
+ --black-white-900: #050505;
18
+ --black-white-950: #000000;
19
+ --black-white-default: #333333;
20
+ --storm-dust: #3d3d3d;
21
+ --storm-dust-950: #262626;
22
+ --babylai-primary-color: #ad49e1;
23
+ --babylai-primary-color-100: #f6ecfc;
24
+ --babylai-primary-color-200: #deb6f3;
25
+ --babylai-primary-color-300: #d49cee;
26
+ --babylai-primary-color-400: #c57fea;
27
+ --babylai-primary-color-500: #ad49e1;
28
+ --babylai-primary-color-600: #672b87;
29
+ --babylai-primary-color-700: #451d5a;
30
+ --babylai-primary-color-800: #220e2d;
31
+ --babylai-primary-color-900: #110716;
32
+ --babylai-primary-color-950: #0a0310;
21
33
 
22
- .babylai-rotate-180 {
23
- transform: rotate(180deg) !important;
34
+ /* Theme-aware colors - Light mode defaults */
35
+ --background: #ffffff;
36
+ --foreground: #333333;
37
+ --card: #ffffff;
38
+ --card-foreground: #333333;
39
+ --popover: #ffffff;
40
+ --popover-foreground: #333333;
41
+ --secondary: #f3f3f3;
42
+ --secondary-foreground: #333333;
43
+ --muted: #f3f3f3;
44
+ --muted-foreground: #606060;
45
+ --accent: #f3f3f3;
46
+ --accent-foreground: #333333;
47
+ --destructive: #ef4444;
48
+ --destructive-foreground: #ffffff;
49
+ --border: #e2e2e2;
50
+ --input: #e2e2e2;
51
+ --ring: var(--babylai-primary-color);
52
+ --radius: 0.5rem;
24
53
  }
25
54
 
26
- img,
27
- svg,
28
- {
29
- display: block;
30
- vertical-align: middle;
31
- }
55
+ /* Tailwind v4 Theme Configuration - must come after @import */
56
+ /* Using actual color values for build-time, CSS variables will override at runtime */
57
+ /* postcss-disable-next-line */
58
+ @theme {
59
+ /* Font Family - override Tailwind's default sans font */
60
+ --font-sans: "Cairo", sans-serif;
32
61
 
33
- img {
34
- max-width: 100%;
35
- height: auto;
36
- }
62
+ /* Primary Colors - default light theme values */
63
+ --color-primary: var(--babylai-primary-color);
64
+ --color-primary-100: var(--babylai-primary-color-100);
65
+ --color-primary-200: var(--babylai-primary-color-200);
66
+ --color-primary-300: var(--babylai-primary-color-300);
67
+ --color-primary-400: var(--babylai-primary-color-400);
68
+ --color-primary-500: var(--babylai-primary-color-500);
69
+ --color-primary-600: var(--babylai-primary-color-600);
70
+ --color-primary-700: var(--babylai-primary-color-700);
71
+ --color-primary-800: var(--babylai-primary-color-800);
72
+ --color-primary-900: var(--babylai-primary-color-900);
73
+ --color-primary-950: var(--babylai-primary-color-950);
74
+
75
+ /* Theme Colors - default light theme values */
76
+ --color-background: var(--background);
77
+ --color-foreground: var(--foreground);
78
+ --color-card: var(--card);
79
+ --color-card-foreground: var(--card-foreground);
80
+ --color-popover: var(--popover);
81
+ --color-popover-foreground: var(--popover-foreground);
82
+ --color-secondary: var(--secondary);
83
+ --color-secondary-foreground: var(--secondary-foreground);
84
+ --color-muted: var(--muted);
85
+ --color-muted-foreground: var(--muted-foreground);
86
+ --color-accent: var(--accent);
87
+ --color-accent-foreground: var(--accent-foreground);
88
+ --color-destructive: var(--destructive);
89
+ --color-destructive-foreground: var(--destructive-foreground);
90
+ --color-border: var(--border);
91
+ --color-input: var(--input);
92
+ --color-ring: var(--ring);
93
+
94
+ /* Black-White Scale */
95
+ --color-black-white-50: var(--black-white-50);
96
+ --color-black-white-100: var(--black-white-100);
97
+ --color-black-white-200: var(--black-white-200);
98
+ --color-black-white-300: var(--black-white-300);
99
+ --color-black-white-400: var(--black-white-400);
100
+ --color-black-white-500: var(--black-white-500);
101
+ --color-black-white-600: var(--black-white-600);
102
+ --color-black-white-700: var(--black-white-700);
103
+ --color-black-white-800: var(--black-white-800);
104
+ --color-black-white-900: var(--black-white-900);
105
+ --color-black-white-950: var(--black-white-950);
106
+
107
+ /* Storm Dust Colors */
108
+ --color-storm-dust: var(--storm-dust);
109
+ --color-storm-dust-950: var(--storm-dust-950);
110
+
111
+ --animate-wave: wave 1.3s ease-in-out infinite;
112
+ --animate-circular-progress: circular-progress 2s ease-in-out infinite;
113
+ --animate-float: float 3s infinite ease-in-out;
114
+
115
+ @keyframes wave {
37
116
 
38
- /* Floating-message styles */
39
- @keyframes float {
117
+ 0%,
118
+ 60%,
119
+ 100% {
120
+ transform: initial;
121
+ }
40
122
 
41
- 0%,
42
- 100% {
43
- transform: translateY(0);
123
+ 30% {
124
+ transform: translateY(-10px);
125
+ }
44
126
  }
45
127
 
46
- 50% {
47
- transform: translateY(-10px);
128
+ @keyframes circular-progress {
129
+ 0% {
130
+ stroke-dashoffset: 100.48;
131
+ }
132
+
133
+ 50% {
134
+ stroke-dashoffset: 25.12;
135
+ }
136
+
137
+ 100% {
138
+ stroke-dashoffset: 100.48;
139
+ }
48
140
  }
49
- }
50
141
 
51
- .babylai-bg-linear-to-b {
52
- background: var(--babylai-primary-color);
53
- background: linear-gradient(180deg, var(--babylai-primary-color) 1%, color-mix(in srgb, var(--babylai-primary-color) 1%, transparent) 100%);
142
+ @keyframes float {
143
+
144
+ 0%,
145
+ 100% {
146
+ transform: translateY(0);
147
+ }
148
+
149
+ 50% {
150
+ transform: translateY(-10px);
151
+ }
152
+ }
54
153
  }
55
154
 
56
- /* Dark mode via Tailwind .dark class */
57
- .dark .babylai-bg-linear-to-b {
58
- background: var(--babylai-storm-dust-950);
59
- background: linear-gradient(180deg, var(--babylai-storm-dust-950) 1%, color-mix(in srgb, var(--babylai-storm-dust-950) 60%, transparent) 100%);
155
+ /* Re-scope primary theme vars on widget root so primaryColor prop (inline style) wins over :root.
156
+ Tailwind v4 with prefix(babylai) emits --babylai-color-primary-* in utilities, so we set
157
+ prefixed names here so they resolve from this element. */
158
+ .babylai-theme-root {
159
+ --babylai-color-primary: var(--babylai-primary-color);
160
+ --babylai-color-primary-100: var(--babylai-primary-color-100);
161
+ --babylai-color-primary-200: var(--babylai-primary-color-200);
162
+ --babylai-color-primary-300: var(--babylai-primary-color-300);
163
+ --babylai-color-primary-400: var(--babylai-primary-color-400);
164
+ --babylai-color-primary-500: var(--babylai-primary-color-500);
165
+ --babylai-color-primary-600: var(--babylai-primary-color-600);
166
+ --babylai-color-primary-700: var(--babylai-primary-color-700);
167
+ --babylai-color-primary-800: var(--babylai-primary-color-800);
168
+ --babylai-color-primary-900: var(--babylai-primary-color-900);
169
+ --babylai-color-primary-950: var(--babylai-primary-color-950);
170
+ --ring: var(--babylai-primary-color);
171
+ --color-ring: var(--babylai-primary-color);
60
172
  }
61
173
 
62
- /* Dark mode via browser preference */
174
+ /* Dark theme colors */
63
175
  @media (prefers-color-scheme: dark) {
64
- .babylai-bg-linear-to-b {
65
- background: var(--babylai-storm-dust-950);
66
- background: linear-gradient(180deg, var(--babylai-storm-dust-950) 1%, color-mix(in srgb, var(--babylai-storm-dust-950) 60%, transparent) 100%);
176
+ :root {
177
+ --black-white-50: #000000;
178
+ --black-white-100: #050505;
179
+ --black-white-200: #0a0a0a;
180
+ --black-white-300: #171717;
181
+ --black-white-400: #1f1f1f;
182
+ --black-white-500: #333333;
183
+ --black-white-600: #606060;
184
+ --black-white-700: #919191;
185
+ --black-white-800: #e2e2e2;
186
+ --black-white-900: #f3f3f3;
187
+ --black-white-950: #ffffff;
188
+ --black-white-default: #e2e2e2;
189
+ --storm-dust: #262626;
190
+ --storm-dust-950: #3d3d3d;
191
+
192
+ /* Theme-aware colors - Dark mode (WCAG 2.1 AA compliant) */
193
+ --background: #121212;
194
+ /* Better than pure black, reduces eye strain */
195
+ --foreground: #ffffff;
196
+ /* High contrast white text */
197
+ --card: #262626;
198
+ /* Slightly lighter than background for depth */
199
+ --card-foreground: #ffffff;
200
+ /* High contrast white text */
201
+ --popover: #1e1e1e;
202
+ /* Consistent with card background */
203
+ --popover-foreground: #ffffff;
204
+ /* High contrast white text */
205
+ --secondary: #3d3d3d;
206
+ /* Accessible secondary background */
207
+ --secondary-foreground: #ffffff;
208
+ /* High contrast white text */
209
+ --muted: #2a2a2a;
210
+ /* Accessible muted background */
211
+ --muted-foreground: #b3b3b3;
212
+ /* WCAG AA compliant contrast (4.5:1) */
213
+ --accent: #2a2a2a;
214
+ /* Accessible accent background */
215
+ --accent-foreground: #ffffff;
216
+ /* High contrast white text */
217
+ --destructive: #ff6b6b;
218
+ /* More accessible red with better contrast */
219
+ --destructive-foreground: #ffffff;
220
+ /* High contrast white text */
221
+ --border: #404040;
222
+ /* Better contrast for borders */
223
+ --input: var(--card);
224
+ /* Accessible input background */
225
+ --ring: var(--babylai-primary-color);
226
+ /* Keep primary color for focus rings */
67
227
  }
228
+ }
229
+
230
+ .bg-header {
231
+ background: linear-gradient(171deg,
232
+ color-mix(in srgb, var(--babylai-primary-color) 25%, transparent) -131.06%,
233
+ color-mix(in srgb, var(--black-white-50) 25%, transparent) 89.82%);
68
234
  }