@memori.ai/memori-react 7.26.0 → 7.26.2

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 (50) hide show
  1. package/CHANGELOG.md +29 -0
  2. package/dist/components/ChatBubble/ChatBubble.js +17 -9
  3. package/dist/components/ChatBubble/ChatBubble.js.map +1 -1
  4. package/dist/components/MediaWidget/LinkItemWidget.css +1 -0
  5. package/dist/components/MediaWidget/MediaItemWidget.css +10 -0
  6. package/dist/components/MediaWidget/MediaItemWidget.d.ts +6 -2
  7. package/dist/components/MediaWidget/MediaItemWidget.js +49 -11
  8. package/dist/components/MediaWidget/MediaItemWidget.js.map +1 -1
  9. package/dist/components/MediaWidget/MediaWidget.d.ts +3 -1
  10. package/dist/components/UploadButton/UploadButton.js +16 -5
  11. package/dist/components/UploadButton/UploadButton.js.map +1 -1
  12. package/dist/components/UploadButton/UploadImages/UploadImages.js +7 -16
  13. package/dist/components/UploadButton/UploadImages/UploadImages.js.map +1 -1
  14. package/dist/components/layouts/HiddenChat.js +1 -16
  15. package/dist/components/layouts/HiddenChat.js.map +1 -1
  16. package/dist/components/ui/Card.d.ts +1 -0
  17. package/dist/components/ui/Card.js +2 -2
  18. package/dist/components/ui/Card.js.map +1 -1
  19. package/esm/components/ChatBubble/ChatBubble.js +17 -9
  20. package/esm/components/ChatBubble/ChatBubble.js.map +1 -1
  21. package/esm/components/MediaWidget/LinkItemWidget.css +1 -0
  22. package/esm/components/MediaWidget/MediaItemWidget.css +10 -0
  23. package/esm/components/MediaWidget/MediaItemWidget.d.ts +6 -2
  24. package/esm/components/MediaWidget/MediaItemWidget.js +50 -12
  25. package/esm/components/MediaWidget/MediaItemWidget.js.map +1 -1
  26. package/esm/components/MediaWidget/MediaWidget.d.ts +3 -1
  27. package/esm/components/UploadButton/UploadButton.js +16 -5
  28. package/esm/components/UploadButton/UploadButton.js.map +1 -1
  29. package/esm/components/UploadButton/UploadImages/UploadImages.js +7 -16
  30. package/esm/components/UploadButton/UploadImages/UploadImages.js.map +1 -1
  31. package/esm/components/layouts/HiddenChat.js +1 -16
  32. package/esm/components/layouts/HiddenChat.js.map +1 -1
  33. package/esm/components/ui/Card.d.ts +1 -0
  34. package/esm/components/ui/Card.js +2 -2
  35. package/esm/components/ui/Card.js.map +1 -1
  36. package/package.json +1 -1
  37. package/src/components/Chat/Chat.stories.tsx +12 -0
  38. package/src/components/ChatBubble/ChatBubble.stories.tsx +203 -26
  39. package/src/components/ChatBubble/ChatBubble.tsx +14 -11
  40. package/src/components/MediaWidget/LinkItemWidget.css +1 -0
  41. package/src/components/MediaWidget/MediaItemWidget.css +10 -0
  42. package/src/components/MediaWidget/MediaItemWidget.tsx +136 -118
  43. package/src/components/MediaWidget/MediaWidget.test.tsx +2 -1
  44. package/src/components/MediaWidget/MediaWidget.tsx +1 -1
  45. package/src/components/UploadButton/UploadButton.tsx +56 -34
  46. package/src/components/UploadButton/UploadImages/UploadImages.tsx +7 -24
  47. package/src/components/UploadButton/__snapshots__/UploadButton.test.tsx.snap +0 -1
  48. package/src/components/layouts/HiddenChat.tsx +1 -16
  49. package/src/components/layouts/layouts.stories.tsx +1 -0
  50. package/src/components/ui/Card.tsx +3 -0
@@ -102,11 +102,8 @@ const ChatBubble: React.FC<Props> = ({
102
102
  const functionCacheData = message.media?.find(
103
103
  m => m.properties?.functionCache === 'true'
104
104
  );
105
-
106
- // Render MathJax whenever message content changes
107
105
  useLayoutEffect(() => {
108
106
  if (typeof window !== 'undefined' && !message.fromUser) {
109
- // Allow a short delay for the DOM to update
110
107
  const timer = setTimeout(() => {
111
108
  if (window.MathJax && window.MathJax.typesetPromise) {
112
109
  try {
@@ -114,9 +111,21 @@ const ChatBubble: React.FC<Props> = ({
114
111
  '.memori-chat--bubble-content'
115
112
  );
116
113
  if (elements.length > 0) {
114
+ // Salva la posizione di scroll corrente
115
+ const scrollContainer = document.querySelector('.memori-chat--history');
116
+ const currentScrollTop = scrollContainer?.scrollTop || 0;
117
+ const currentScrollHeight = scrollContainer?.scrollHeight || 0;
118
+
117
119
  window.MathJax.typesetPromise([
118
120
  '.memori-chat--bubble-content',
119
- ]).catch(err =>
121
+ ]).then(() => {
122
+ // Ripristina la posizione di scroll dopo il rendering MathJax
123
+ if (scrollContainer) {
124
+ const newScrollHeight = scrollContainer.scrollHeight;
125
+ const heightDifference = newScrollHeight - currentScrollHeight;
126
+ scrollContainer.scrollTop = currentScrollTop + heightDifference;
127
+ }
128
+ }).catch(err =>
120
129
  console.error('MathJax typesetting failed:', err)
121
130
  );
122
131
  }
@@ -125,16 +134,10 @@ const ChatBubble: React.FC<Props> = ({
125
134
  }
126
135
  }
127
136
  }, 100);
128
-
137
+
129
138
  return () => clearTimeout(timer);
130
139
  }
131
140
  }, [message.text, message.fromUser, renderedText]);
132
-
133
- console.log(
134
- 'message',
135
- message.media?.find(m => m.properties?.functionCache)
136
- );
137
-
138
141
  return (
139
142
  <>
140
143
  {(message.initial || isFirst) && (
@@ -1,6 +1,7 @@
1
1
  .memori-link-items--grid {
2
2
  display: grid;
3
3
  padding: 1rem;
4
+ direction: rtl;
4
5
  grid-auto-flow: dense;
5
6
  grid-gap: 1rem;
6
7
  grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
@@ -1,6 +1,7 @@
1
1
  .memori-media-items--grid {
2
2
  display: grid;
3
3
  padding: 1rem;
4
+ direction: rtl;
4
5
  grid-auto-flow: dense;
5
6
  grid-gap: 1rem;
6
7
  grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
@@ -143,3 +144,12 @@ a.memori-media-item--link {
143
144
  border-color: #fff;
144
145
  color: #fff;
145
146
  }
147
+
148
+ .memori-media-item-preview--modal {
149
+ z-index: 10000;
150
+ width: 100%;
151
+ max-width: 80%;
152
+ height: 100%;
153
+ max-height: 80%;
154
+ background: #fff;
155
+ }
@@ -12,9 +12,10 @@ import FilePdf from '../icons/FilePdf';
12
12
  import FileExcel from '../icons/FileExcel';
13
13
  import FileWord from '../icons/FileWord';
14
14
  import { Transition } from '@headlessui/react';
15
+ import { stripHTML } from '../../helpers/utils';
15
16
 
16
17
  export interface Props {
17
- items: Medium[];
18
+ items: (Medium & { type?: string })[];
18
19
  sessionID?: string;
19
20
  tenantID?: string;
20
21
  translateTo?: string;
@@ -35,7 +36,7 @@ export const RenderMediaItem = ({
35
36
  customMediaRenderer,
36
37
  }: {
37
38
  isChild?: boolean;
38
- item: Medium;
39
+ item: Medium & { type: string };
39
40
  sessionID?: string;
40
41
  tenantID?: string;
41
42
  preview?: boolean;
@@ -44,6 +45,8 @@ export const RenderMediaItem = ({
44
45
  onClick?: (mediumID: string) => void;
45
46
  customMediaRenderer?: (mimeType: string) => JSX.Element | null;
46
47
  }) => {
48
+ const [modalOpen, setModalOpen] = useState(false);
49
+
47
50
  const url = getResourceUrl({
48
51
  resourceURI: item.url,
49
52
  sessionID,
@@ -58,6 +61,128 @@ export const RenderMediaItem = ({
58
61
  return customRenderer;
59
62
  }
60
63
 
64
+ const renderMediaContent = (item: Medium) => {
65
+ switch (item.mimeType) {
66
+ case 'image/jpeg':
67
+ case 'image/png':
68
+ case 'image/jpg':
69
+ case 'image/gif':
70
+ return isImageRGB ? (
71
+ <picture className="memori-media-item--figure">
72
+ <div
73
+ className="memori-media-item--rgb-item"
74
+ style={{
75
+ backgroundColor: item.url,
76
+ }}
77
+ />
78
+ {item.title && (
79
+ <figcaption className="memori-media-item--figure-caption">
80
+ {item.title}
81
+ </figcaption>
82
+ )}
83
+ </picture>
84
+ ) : (
85
+ <picture className="memori-media-item--figure">
86
+ {!preview && (
87
+ <source
88
+ srcSet={[url, item.url, item.content].join(', ')}
89
+ type={item.mimeType}
90
+ />
91
+ )}
92
+ <img
93
+ alt={item.title}
94
+ src="data:image/png;base64,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"
95
+ />
96
+ {item.title && (
97
+ <figcaption className="memori-media-item--figure-caption">
98
+ {item.title}
99
+ </figcaption>
100
+ )}
101
+ </picture>
102
+ );
103
+
104
+ case 'application/msword':
105
+ case 'application/vnd.openxmlformats-officedocument.wordprocessingml.document':
106
+ return <FileWord className="memori-media-item--icon" />;
107
+
108
+ case 'application/vnd.ms-excel':
109
+ case 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet':
110
+ return <FileExcel className="memori-media-item--icon" />;
111
+
112
+ case 'application/pdf':
113
+ return <FilePdf className="memori-media-item--icon" />;
114
+
115
+ case 'video/mp4':
116
+ case 'video/quicktime':
117
+ case 'video/avi':
118
+ case 'video/mpeg':
119
+ return (
120
+ <video
121
+ style={{ width: '100%', height: '100%' }}
122
+ controls
123
+ src={url}
124
+ title={item.title}
125
+ >
126
+ {item.mimeType === 'video/quicktime' && (
127
+ <source src={item.url} type="video/mp4" />
128
+ )}
129
+ <source src={item.url} type={item.mimeType} />
130
+ Your browser does not support this video format.
131
+ <br />
132
+ <a href={item.url} target="_blank" rel="noopener noreferrer">
133
+ Download the video
134
+ </a>
135
+ </video>
136
+ );
137
+
138
+ case 'audio/mpeg3':
139
+ case 'audio/wav':
140
+ case 'audio/mpeg':
141
+ return (
142
+ <audio style={{ width: '100%', height: '100%' }} controls src={url} />
143
+ );
144
+
145
+ case 'model/gltf-binary':
146
+ return (
147
+ <ModelViewer
148
+ src={url}
149
+ alt=""
150
+ poster="data:image/png;base64,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"
151
+ />
152
+ );
153
+
154
+ default:
155
+ return <File className="memori-media-item--icon" />;
156
+ }
157
+ };
158
+
159
+ if (!item.url && item?.type === 'document' && item.content) {
160
+ return (
161
+ <>
162
+ <a
163
+ className="memori-media-item--link"
164
+ href="#"
165
+ onClick={e => {
166
+ e.preventDefault();
167
+ setModalOpen(true);
168
+ }}
169
+ title={item.title}
170
+ >
171
+ <Card hoverable cover={renderMediaContent(item)} title={item.title} />
172
+ </a>
173
+
174
+ <Modal
175
+ open={modalOpen}
176
+ onClose={() => setModalOpen(false)}
177
+ title={item.title}
178
+ className="memori-media-item-preview--modal"
179
+ >
180
+ <pre>{stripHTML(item.content)}</pre>
181
+ </Modal>
182
+ </>
183
+ );
184
+ }
185
+
61
186
  const isImageRGB =
62
187
  item.url?.startsWith('rgb(') || item.url?.startsWith('rgba(');
63
188
 
@@ -70,21 +195,7 @@ export const RenderMediaItem = ({
70
195
  <Card
71
196
  hoverable
72
197
  className="memori-media-item--card memori-media-item--image"
73
- cover={
74
- <picture className="memori-media-item--figure">
75
- <div
76
- className="memori-media-item--rgb-item"
77
- style={{
78
- backgroundColor: item.url,
79
- }}
80
- />
81
- {item.title && (
82
- <figcaption className="memori-media-item--figure-caption">
83
- {item.title}
84
- </figcaption>
85
- )}
86
- </picture>
87
- }
198
+ cover={renderMediaContent(item)}
88
199
  />
89
200
  ) : (
90
201
  <a
@@ -106,65 +217,15 @@ export const RenderMediaItem = ({
106
217
  <Card
107
218
  hoverable
108
219
  className="memori-media-item--card memori-media-item--image"
109
- cover={
110
- <picture className="memori-media-item--figure">
111
- {!preview && (
112
- <source
113
- srcSet={[url, item.url, item.content].join(', ')}
114
- type={item.mimeType}
115
- />
116
- )}
117
- <img
118
- alt={item.title}
119
- src="data:image/png;base64,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"
120
- />
121
- {item.title && (
122
- <figcaption className="memori-media-item--figure-caption">
123
- {item.title}
124
- </figcaption>
125
- )}
126
- </picture>
127
- }
220
+ cover={renderMediaContent(item)}
128
221
  />
129
222
  </a>
130
223
  );
131
224
 
132
225
  case 'application/msword':
133
226
  case 'application/vnd.openxmlformats-officedocument.wordprocessingml.document':
134
- return (
135
- <a
136
- className="memori-media-item--link"
137
- href={url}
138
- target="_blank"
139
- rel="noopener noreferrer"
140
- title={item.title}
141
- >
142
- <Card
143
- hoverable
144
- cover={<FileWord className="memori-media-item--icon" />}
145
- title={item.title}
146
- />
147
- </a>
148
- );
149
-
150
227
  case 'application/vnd.ms-excel':
151
228
  case 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet':
152
- return (
153
- <a
154
- className="memori-media-item--link"
155
- href={url}
156
- target="_blank"
157
- rel="noopener noreferrer"
158
- title={item.title}
159
- >
160
- <Card
161
- hoverable
162
- cover={<FileExcel className="memori-media-item--icon" />}
163
- title={item.title}
164
- />
165
- </a>
166
- );
167
-
168
229
  case 'application/pdf':
169
230
  return (
170
231
  <a
@@ -174,11 +235,7 @@ export const RenderMediaItem = ({
174
235
  rel="noopener noreferrer"
175
236
  title={item.title}
176
237
  >
177
- <Card
178
- hoverable
179
- cover={<FilePdf className="memori-media-item--icon" />}
180
- title={item.title}
181
- />
238
+ <Card hoverable cover={renderMediaContent(item)} title={item.title} />
182
239
  </a>
183
240
  );
184
241
 
@@ -203,28 +260,7 @@ export const RenderMediaItem = ({
203
260
  rel="noopener noreferrer"
204
261
  title={item.title}
205
262
  >
206
- <Card
207
- hoverable
208
- cover={
209
- <video
210
- style={{ width: '100%', height: '100%' }}
211
- controls
212
- src={url}
213
- title={item.title}
214
- >
215
- {item.mimeType === 'video/quicktime' && (
216
- <source src={item.url} type="video/mp4" />
217
- )}
218
- <source src={item.url} type={item.mimeType} />
219
- Your browser does not support this video format.
220
- <br />
221
- <a href={item.url} target="_blank" rel="noopener noreferrer">
222
- Download the video
223
- </a>
224
- </video>
225
- }
226
- title={item.title}
227
- />
263
+ <Card hoverable cover={renderMediaContent(item)} title={item.title} />
228
264
  </a>
229
265
  );
230
266
 
@@ -239,28 +275,12 @@ export const RenderMediaItem = ({
239
275
  rel="noopener noreferrer"
240
276
  title={item.title}
241
277
  >
242
- <Card
243
- hoverable
244
- cover={
245
- <audio
246
- style={{ width: '100%', height: '100%' }}
247
- controls
248
- src={url}
249
- />
250
- }
251
- title={item.title}
252
- />
278
+ <Card hoverable cover={renderMediaContent(item)} title={item.title} />
253
279
  </a>
254
280
  );
255
281
 
256
282
  case 'model/gltf-binary':
257
- return (
258
- <ModelViewer
259
- src={url}
260
- alt=""
261
- poster="data:image/png;base64,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"
262
- />
263
- );
283
+ return <Card hoverable cover={renderMediaContent(item)} />;
264
284
 
265
285
  default:
266
286
  return (
@@ -271,11 +291,7 @@ export const RenderMediaItem = ({
271
291
  rel="noopener noreferrer"
272
292
  title={item.title}
273
293
  >
274
- <Card
275
- hoverable
276
- cover={<File className="memori-media-item--icon" />}
277
- title={item.title}
278
- />
294
+ <Card hoverable cover={renderMediaContent(item)} title={item.title} />
279
295
  </a>
280
296
  );
281
297
  }
@@ -373,6 +389,7 @@ const MediaItemWidget: React.FC<Props> = ({
373
389
  title: item.title,
374
390
  url: item.url,
375
391
  content: item.content,
392
+ type: 'document',
376
393
  }}
377
394
  customMediaRenderer={customMediaRenderer}
378
395
  />
@@ -422,6 +439,7 @@ const MediaItemWidget: React.FC<Props> = ({
422
439
  title: openModalMedium.title,
423
440
  url: openModalMedium.url,
424
441
  content: openModalMedium.content,
442
+ type: 'document',
425
443
  }}
426
444
  customMediaRenderer={customMediaRenderer}
427
445
  />
@@ -7,11 +7,12 @@ import { render } from '@testing-library/react';
7
7
  import { medium } from '../../mocks/data';
8
8
  import MediaWidget from './MediaWidget';
9
9
 
10
- const linkMedium: Medium = {
10
+ const linkMedium: Medium & { type?: string } = {
11
11
  mediumID: '95226d7e-7bae-465e-8b80-995587bb5971',
12
12
  mimeType: 'text/html',
13
13
  title: 'Memori Srl',
14
14
  url: 'https://memori.ai/en',
15
+ type: 'document',
15
16
  };
16
17
 
17
18
  const hint: TranslatedHint = {
@@ -13,7 +13,7 @@ import { useTranslation } from 'react-i18next';
13
13
  export interface Props {
14
14
  hints?: TranslatedHint[];
15
15
  links?: Medium[];
16
- media?: Medium[];
16
+ media?: (Medium & { type?: string })[];
17
17
  simulateUserPrompt?: (item: string, translatedItem?: string) => void;
18
18
  sessionID?: string;
19
19
  baseUrl?: string;
@@ -52,9 +52,13 @@ const UploadButton: React.FC<UploadManagerProps> = ({
52
52
  const imageRef = useRef<HTMLDivElement>(null);
53
53
 
54
54
  // Calculate image count and remaining slots
55
- const currentImageCount = documentPreviewFiles.filter(file => file.type === 'image').length;
55
+ const currentImageCount = documentPreviewFiles.filter(
56
+ file => file.type === 'image'
57
+ ).length;
56
58
  const remainingSlots = MAX_IMAGES - currentImageCount;
57
- const currentDocumentCount = documentPreviewFiles.filter(file => file.type === 'document').length;
59
+ const currentDocumentCount = documentPreviewFiles.filter(
60
+ file => file.type === 'document'
61
+ ).length;
58
62
  const remainingDocumentSlots = MAX_DOCUMENTS - currentDocumentCount;
59
63
  const hasReachedImageLimit = remainingSlots <= 0;
60
64
  const hasReachedDocumentLimit = remainingDocumentSlots <= 0;
@@ -111,16 +115,25 @@ const UploadButton: React.FC<UploadManagerProps> = ({
111
115
  // For simplicity, we only take the first file
112
116
  const file = files[0];
113
117
 
114
- // Format content with XML tags to improve readability for LLM
115
- const formattedContent = `<Documento allegato al messaggio: ${file.name}>
116
- ${file.content}
117
- </Documento allegato al messaggio: ${file.name}>`;
118
+ // Funzione helper per fare escape dell'HTML nei valori degli attributi
119
+ const escapeAttributeValue = (text: string) => {
120
+ return text
121
+ .replace(/&/g, '&amp;')
122
+ .replace(/"/g, '&quot;')
123
+ .replace(/'/g, '&#39;')
124
+ .replace(/</g, '&lt;')
125
+ .replace(/>/g, '&gt;');
126
+ };
118
127
 
128
+ // ✅ MIGLIORE SOLUZIONE: Tag XML valido con attributi chiari
129
+ const escapedFileName = escapeAttributeValue(file.name);
130
+ const formattedContent = `<document_attachment filename="${escapedFileName}" type="${file.mimeType}">
131
+ ${file.content}
132
+ </document_attachment>`;
119
133
  //keep just the images in the documentPreviewFiles
120
134
  const imageFiles = documentPreviewFiles.filter(
121
135
  (file: any) => file.type === 'image'
122
136
  );
123
-
124
137
  // Replace existing file with new one
125
138
  setDocumentPreviewFiles([
126
139
  {
@@ -156,26 +169,28 @@ ${file.content}
156
169
  closeMenu();
157
170
  return;
158
171
  }
159
-
172
+
160
173
  if (!isMediaAccepted) {
161
174
  addError({
162
- message: t('upload.mediaNotAccepted') ?? 'Media uploads are not accepted',
175
+ message:
176
+ t('upload.mediaNotAccepted') ?? 'Media uploads are not accepted',
163
177
  severity: 'info',
164
178
  });
165
179
  closeMenu();
166
180
  return;
167
181
  }
168
-
182
+
169
183
  if (hasReachedImageLimit) {
170
184
  addError({
171
- message: t('upload.maxImagesReached', { max: MAX_IMAGES }) ??
185
+ message:
186
+ t('upload.maxImagesReached', { max: MAX_IMAGES }) ??
172
187
  `Maximum ${MAX_IMAGES} images already uploaded`,
173
188
  severity: 'warning',
174
189
  });
175
190
  closeMenu();
176
191
  return;
177
192
  }
178
-
193
+
179
194
  // If all checks pass, click the button in UploadImages component
180
195
  const imageButtonElement = imageRef.current?.querySelector('button');
181
196
  if (imageButtonElement) {
@@ -213,13 +228,14 @@ ${file.content}
213
228
  <UploadIcon className="memori--upload-icon" />
214
229
  )}
215
230
  </button>
216
-
231
+
217
232
  {/* Image count indicator - moved here from UploadImages */}
218
233
  {currentImageCount > 0 && (
219
- <div className={cx(
220
- 'memori--image-count',
221
- { 'memori--image-count-full': hasReachedImageLimit }
222
- )}>
234
+ <div
235
+ className={cx('memori--image-count', {
236
+ 'memori--image-count-full': hasReachedImageLimit,
237
+ })}
238
+ >
223
239
  {currentImageCount}/{MAX_IMAGES}
224
240
  </div>
225
241
  )}
@@ -228,9 +244,8 @@ ${file.content}
228
244
  {menuOpen && (
229
245
  <div className="memori--upload-menu" ref={menuRef}>
230
246
  <div
231
- className={cx('memori--upload-menu-item', {
232
- 'memori--upload-menu-item--disabled':
233
- !authToken || hasReachedDocumentLimit,
247
+ className={cx('memori--upload-menu-item', {
248
+ 'memori--upload-menu-item--disabled': hasReachedDocumentLimit,
234
249
  })}
235
250
  onClick={handleDocumentClick}
236
251
  >
@@ -241,8 +256,9 @@ ${file.content}
241
256
  <span className="memori--upload-slots-info">
242
257
  {hasReachedDocumentLimit
243
258
  ? ` (${t('upload.maxReached') ?? 'Max reached'})`
244
- : ` (${remainingDocumentSlots} ${t('upload.remaining') ?? 'remaining'})`
245
- }
259
+ : ` (${remainingDocumentSlots} ${
260
+ t('upload.remaining') ?? 'remaining'
261
+ })`}
246
262
  </span>
247
263
  )}
248
264
  </span>
@@ -251,19 +267,21 @@ ${file.content}
251
267
  <div
252
268
  className={cx('memori--upload-menu-item', {
253
269
  'memori--upload-menu-item--disabled':
254
- !isMediaAccepted || !authToken || hasReachedImageLimit,
270
+ !authToken || !isMediaAccepted || hasReachedImageLimit,
255
271
  })}
256
272
  onClick={handleImageClick}
257
273
  title={
258
274
  !authToken
259
275
  ? t('upload.loginRequired') ?? 'Login Required'
260
276
  : !isMediaAccepted
261
- ? t('upload.mediaNotAccepted') ?? 'Media uploads not accepted'
262
- : hasReachedImageLimit
263
- ? t('upload.maxImagesReached', { max: MAX_IMAGES }) ?? `Maximum ${MAX_IMAGES} images already uploaded`
264
- : remainingSlots === 1
265
- ? t('upload.lastImageSlot') ?? 'Upload last image'
266
- : t('upload.uploadImage', { remaining: remainingSlots }) ?? `Upload image (${remainingSlots} remaining)`
277
+ ? t('upload.mediaNotAccepted') ?? 'Media uploads not accepted'
278
+ : hasReachedImageLimit
279
+ ? t('upload.maxImagesReached', { max: MAX_IMAGES }) ??
280
+ `Maximum ${MAX_IMAGES} images already uploaded`
281
+ : remainingSlots === 1
282
+ ? t('upload.lastImageSlot') ?? 'Upload last image'
283
+ : t('upload.uploadImage', { remaining: remainingSlots }) ??
284
+ `Upload image (${remainingSlots} remaining)`
267
285
  }
268
286
  >
269
287
  <ImageIcon className="memori--upload-menu-icon-image" />
@@ -273,8 +291,9 @@ ${file.content}
273
291
  <span className="memori--upload-slots-info">
274
292
  {hasReachedImageLimit
275
293
  ? ` (${t('upload.maxReached') ?? 'Max reached'})`
276
- : ` (${remainingSlots} ${t('upload.remaining') ?? 'remaining'})`
277
- }
294
+ : ` (${remainingSlots} ${
295
+ t('upload.remaining') ?? 'remaining'
296
+ })`}
278
297
  </span>
279
298
  )}
280
299
  </span>
@@ -284,7 +303,7 @@ ${file.content}
284
303
 
285
304
  {/* Hidden components */}
286
305
  <div className="memori--hidden-uploader" ref={documentRef}>
287
- <UploadDocuments
306
+ <UploadDocuments
288
307
  setDocumentPreviewFiles={handleDocumentFiles}
289
308
  maxDocuments={MAX_DOCUMENTS}
290
309
  documentPreviewFiles={documentPreviewFiles}
@@ -327,7 +346,10 @@ ${file.content}
327
346
  <Alert
328
347
  type="info"
329
348
  title={t('upload.loginRequired') ?? 'Login Required'}
330
- description={t('upload.loginRequiredDescription') ?? 'Please login to upload images'}
349
+ description={
350
+ t('upload.loginRequiredDescription') ??
351
+ 'Please login to upload images'
352
+ }
331
353
  width="350px"
332
354
  onClose={closeMenu}
333
355
  />
@@ -337,4 +359,4 @@ ${file.content}
337
359
  );
338
360
  };
339
361
 
340
- export default UploadButton;
362
+ export default UploadButton;