@memori.ai/memori-react 7.25.0 → 7.26.0

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 (132) hide show
  1. package/CHANGELOG.md +42 -0
  2. package/dist/components/Chat/Chat.d.ts +5 -10
  3. package/dist/components/Chat/Chat.js +3 -3
  4. package/dist/components/Chat/Chat.js.map +1 -1
  5. package/dist/components/ChatBubble/ChatBubble.css +10 -0
  6. package/dist/components/ChatBubble/ChatBubble.d.ts +1 -0
  7. package/dist/components/ChatBubble/ChatBubble.js +16 -20
  8. package/dist/components/ChatBubble/ChatBubble.js.map +1 -1
  9. package/dist/components/ChatInputs/ChatInputs.d.ts +6 -10
  10. package/dist/components/ChatInputs/ChatInputs.js +37 -30
  11. package/dist/components/ChatInputs/ChatInputs.js.map +1 -1
  12. package/dist/components/FilePreview/FilePreview.css +169 -140
  13. package/dist/components/FilePreview/FilePreview.d.ts +2 -6
  14. package/dist/components/FilePreview/FilePreview.js +58 -5
  15. package/dist/components/FilePreview/FilePreview.js.map +1 -1
  16. package/dist/components/MemoriWidget/MemoriWidget.d.ts +2 -1
  17. package/dist/components/MemoriWidget/MemoriWidget.js +15 -21
  18. package/dist/components/MemoriWidget/MemoriWidget.js.map +1 -1
  19. package/dist/components/UploadButton/UploadButton.css +506 -27
  20. package/dist/components/UploadButton/UploadButton.d.ts +14 -11
  21. package/dist/components/UploadButton/UploadButton.js +110 -288
  22. package/dist/components/UploadButton/UploadButton.js.map +1 -1
  23. package/dist/components/UploadButton/UploadDocuments/UploadDocuments.d.ts +19 -0
  24. package/dist/components/UploadButton/UploadDocuments/UploadDocuments.js +211 -0
  25. package/dist/components/UploadButton/UploadDocuments/UploadDocuments.js.map +1 -0
  26. package/dist/components/UploadButton/UploadImages/UploadImages.d.ts +13 -0
  27. package/dist/components/UploadButton/UploadImages/UploadImages.js +207 -0
  28. package/dist/components/UploadButton/UploadImages/UploadImages.js.map +1 -0
  29. package/dist/components/icons/Bug.d.ts +5 -0
  30. package/dist/components/icons/Bug.js +6 -0
  31. package/dist/components/icons/Bug.js.map +1 -0
  32. package/dist/components/icons/Document.d.ts +5 -0
  33. package/dist/components/icons/Document.js +10 -0
  34. package/dist/components/icons/Document.js.map +1 -0
  35. package/dist/components/icons/Image.d.ts +4 -0
  36. package/dist/components/icons/Image.js +9 -0
  37. package/dist/components/icons/Image.js.map +1 -0
  38. package/dist/components/icons/Preview.d.ts +4 -5
  39. package/dist/components/icons/Preview.js +5 -2
  40. package/dist/components/icons/Preview.js.map +1 -1
  41. package/dist/components/icons/Upload.d.ts +4 -5
  42. package/dist/components/icons/Upload.js +5 -2
  43. package/dist/components/icons/Upload.js.map +1 -1
  44. package/dist/components/layouts/HiddenChat.js +100 -10
  45. package/dist/components/layouts/HiddenChat.js.map +1 -1
  46. package/dist/components/layouts/hidden-chat.css +189 -119
  47. package/dist/locales/de.json +16 -0
  48. package/dist/locales/en.json +24 -0
  49. package/dist/locales/es.json +16 -0
  50. package/dist/locales/fr.json +16 -0
  51. package/dist/locales/it.json +22 -0
  52. package/esm/components/Chat/Chat.d.ts +5 -10
  53. package/esm/components/Chat/Chat.js +3 -3
  54. package/esm/components/Chat/Chat.js.map +1 -1
  55. package/esm/components/ChatBubble/ChatBubble.css +10 -0
  56. package/esm/components/ChatBubble/ChatBubble.d.ts +1 -0
  57. package/esm/components/ChatBubble/ChatBubble.js +16 -20
  58. package/esm/components/ChatBubble/ChatBubble.js.map +1 -1
  59. package/esm/components/ChatInputs/ChatInputs.d.ts +6 -10
  60. package/esm/components/ChatInputs/ChatInputs.js +37 -30
  61. package/esm/components/ChatInputs/ChatInputs.js.map +1 -1
  62. package/esm/components/FilePreview/FilePreview.css +169 -140
  63. package/esm/components/FilePreview/FilePreview.d.ts +2 -6
  64. package/esm/components/FilePreview/FilePreview.js +58 -5
  65. package/esm/components/FilePreview/FilePreview.js.map +1 -1
  66. package/esm/components/MemoriWidget/MemoriWidget.d.ts +2 -1
  67. package/esm/components/MemoriWidget/MemoriWidget.js +15 -21
  68. package/esm/components/MemoriWidget/MemoriWidget.js.map +1 -1
  69. package/esm/components/UploadButton/UploadButton.css +506 -27
  70. package/esm/components/UploadButton/UploadButton.d.ts +14 -11
  71. package/esm/components/UploadButton/UploadButton.js +111 -289
  72. package/esm/components/UploadButton/UploadButton.js.map +1 -1
  73. package/esm/components/UploadButton/UploadDocuments/UploadDocuments.d.ts +19 -0
  74. package/esm/components/UploadButton/UploadDocuments/UploadDocuments.js +208 -0
  75. package/esm/components/UploadButton/UploadDocuments/UploadDocuments.js.map +1 -0
  76. package/esm/components/UploadButton/UploadImages/UploadImages.d.ts +13 -0
  77. package/esm/components/UploadButton/UploadImages/UploadImages.js +204 -0
  78. package/esm/components/UploadButton/UploadImages/UploadImages.js.map +1 -0
  79. package/esm/components/icons/Bug.d.ts +5 -0
  80. package/esm/components/icons/Bug.js +4 -0
  81. package/esm/components/icons/Bug.js.map +1 -0
  82. package/esm/components/icons/Document.d.ts +5 -0
  83. package/esm/components/icons/Document.js +6 -0
  84. package/esm/components/icons/Document.js.map +1 -0
  85. package/esm/components/icons/Image.d.ts +4 -0
  86. package/esm/components/icons/Image.js +5 -0
  87. package/esm/components/icons/Image.js.map +1 -0
  88. package/esm/components/icons/Preview.d.ts +4 -5
  89. package/esm/components/icons/Preview.js +4 -3
  90. package/esm/components/icons/Preview.js.map +1 -1
  91. package/esm/components/icons/Upload.d.ts +4 -5
  92. package/esm/components/icons/Upload.js +4 -3
  93. package/esm/components/icons/Upload.js.map +1 -1
  94. package/esm/components/layouts/HiddenChat.js +101 -11
  95. package/esm/components/layouts/HiddenChat.js.map +1 -1
  96. package/esm/components/layouts/hidden-chat.css +189 -119
  97. package/esm/locales/de.json +16 -0
  98. package/esm/locales/en.json +24 -0
  99. package/esm/locales/es.json +16 -0
  100. package/esm/locales/fr.json +16 -0
  101. package/esm/locales/it.json +22 -0
  102. package/package.json +1 -1
  103. package/src/components/Chat/Chat.tsx +8 -8
  104. package/src/components/ChatBubble/ChatBubble.css +10 -0
  105. package/src/components/ChatBubble/ChatBubble.stories.tsx +25 -0
  106. package/src/components/ChatBubble/ChatBubble.tsx +41 -17
  107. package/src/components/ChatInputs/ChatInputs.tsx +92 -43
  108. package/src/components/FilePreview/FilePreview.css +169 -140
  109. package/src/components/FilePreview/FilePreview.tsx +106 -14
  110. package/src/components/FilePreview/__snapshots__/FilePreview.test.tsx.snap +146 -29
  111. package/src/components/MemoriWidget/MemoriWidget.tsx +14 -22
  112. package/src/components/UploadButton/UploadButton.css +506 -27
  113. package/src/components/UploadButton/UploadButton.stories.tsx +122 -20
  114. package/src/components/UploadButton/UploadButton.test.tsx +1 -1
  115. package/src/components/UploadButton/UploadButton.tsx +264 -454
  116. package/src/components/UploadButton/UploadDocuments/UploadDocuments.tsx +352 -0
  117. package/src/components/UploadButton/UploadImages/UploadImages.tsx +434 -0
  118. package/src/components/UploadButton/__snapshots__/UploadButton.test.tsx.snap +140 -13
  119. package/src/components/icons/Bug.tsx +81 -0
  120. package/src/components/icons/Document.tsx +50 -0
  121. package/src/components/icons/Image.tsx +37 -0
  122. package/src/components/icons/Preview.tsx +28 -22
  123. package/src/components/icons/Upload.tsx +33 -22
  124. package/src/components/layouts/HiddenChat.tsx +143 -7
  125. package/src/components/layouts/__snapshots__/HiddenChat.test.tsx.snap +1 -1
  126. package/src/components/layouts/hidden-chat.css +189 -119
  127. package/src/index.stories.tsx +19 -19
  128. package/src/locales/de.json +16 -0
  129. package/src/locales/en.json +24 -0
  130. package/src/locales/es.json +16 -0
  131. package/src/locales/fr.json +16 -0
  132. package/src/locales/it.json +22 -0
@@ -0,0 +1,352 @@
1
+ import React, { useState, useRef } from 'react';
2
+ import cx from 'classnames';
3
+ import { UploadIcon } from '../../icons/Upload';
4
+ import Spin from '../../ui/Spin';
5
+ import Alert from '../../ui/Alert';
6
+ import { DocumentIcon } from '../../icons/Document';
7
+ import CloseIcon from '../../icons/Close';
8
+ import Button from '../../ui/Button';
9
+ import Modal from '../../ui/Modal';
10
+
11
+ // Types
12
+ type UploadError = {
13
+ message: string;
14
+ severity: 'error' | 'warning' | 'info';
15
+ fileId?: string;
16
+ };
17
+
18
+ type PreviewFile = {
19
+ name: string;
20
+ id: string;
21
+ content: string;
22
+ type: 'document';
23
+ previewUrl?: string;
24
+ uploaded?: boolean;
25
+ error?: boolean;
26
+ };
27
+
28
+ // Constants
29
+ const MAX_FILE_SIZE = 10 * 1024 * 1024; // 10MB
30
+ const MAX_TEXT_LENGTH = 100000; // 100,000 characters
31
+ const PDF_JS_VERSION = '3.11.174';
32
+ const WORKER_URL = `https://cdnjs.cloudflare.com/ajax/libs/pdf.js/${PDF_JS_VERSION}/pdf.worker.min.js`;
33
+ const PDF_JS_URL = `https://cdnjs.cloudflare.com/ajax/libs/pdf.js/${PDF_JS_VERSION}/pdf.min.js`;
34
+ const XLSX_URL = 'https://cdn.sheetjs.com/xlsx-0.20.0/package/dist/xlsx.full.min.js';
35
+
36
+ // Add type definitions for external libraries
37
+ declare global {
38
+ interface Window {
39
+ pdfjsLib: any;
40
+ XLSX: any;
41
+ }
42
+ }
43
+
44
+ // Props interface
45
+ interface UploadDocumentsProps {
46
+ setDocumentPreviewFiles: (files: { name: string; id: string; content: string; mimeType: string }[]) => void;
47
+ maxDocuments?: number;
48
+ documentPreviewFiles: any;
49
+ }
50
+
51
+ const UploadDocuments: React.FC<UploadDocumentsProps> = ({
52
+ setDocumentPreviewFiles,
53
+ maxDocuments,
54
+ documentPreviewFiles,
55
+ }) => {
56
+ // State
57
+ const [isLoading, setIsLoading] = useState(false);
58
+ const [errors, setErrors] = useState<UploadError[]>([]);
59
+ const [selectedFile, setSelectedFile] = useState<PreviewFile | null>(null);
60
+
61
+ // Refs
62
+ const documentInputRef = useRef<HTMLInputElement>(null);
63
+
64
+ // Error handling
65
+ const clearErrors = () => setErrors([]);
66
+
67
+ const removeError = (errorMessage: string) => {
68
+ setErrors(prev => prev.filter(e => e.message !== errorMessage));
69
+ };
70
+
71
+ const addError = (error: UploadError) => {
72
+ setErrors(prev => [...prev, error]);
73
+ setTimeout(() => removeError(error.message), 5000);
74
+ };
75
+
76
+ // Document upload
77
+ const validateDocumentFile = (file: File): boolean => {
78
+ const fileExt = `.${file.name.split('.').pop()?.toLowerCase()}`;
79
+ const ALLOWED_FILE_TYPES = ['.pdf', '.txt', '.json', '.xlsx', '.csv', '.md']; // Added .md
80
+
81
+ if (!ALLOWED_FILE_TYPES.includes(fileExt)) {
82
+ addError({
83
+ message: `File type "${fileExt}" is not supported. Please use: ${ALLOWED_FILE_TYPES.join(', ')}`,
84
+ severity: 'error',
85
+ fileId: file.name,
86
+ });
87
+ return false;
88
+ }
89
+
90
+ if (file.size > MAX_FILE_SIZE) {
91
+ addError({
92
+ message: `File "${file.name}" exceeds ${MAX_FILE_SIZE / 1024 / 1024}MB limit`,
93
+ severity: 'error',
94
+ fileId: file.name,
95
+ });
96
+ return false;
97
+ }
98
+
99
+ return true;
100
+ };
101
+
102
+ const extractTextFromPDF = async (file: File): Promise<string> => {
103
+ try {
104
+ // Load PDF.js if not already loaded
105
+ if (!window.pdfjsLib) {
106
+ await new Promise((resolve, reject) => {
107
+ const script = document.createElement('script');
108
+ script.src = PDF_JS_URL;
109
+ script.onload = () => {
110
+ window.pdfjsLib.GlobalWorkerOptions.workerSrc = WORKER_URL;
111
+ resolve(true);
112
+ };
113
+ script.onerror = reject;
114
+ document.head.appendChild(script);
115
+ });
116
+ }
117
+
118
+ // Extract text from PDF
119
+ const arrayBuffer = await file.arrayBuffer();
120
+ const pdf = await window.pdfjsLib.getDocument({ data: arrayBuffer }).promise;
121
+ let text = '';
122
+
123
+ // Iterate through each page and extract text
124
+ for (let i = 1; i <= pdf.numPages; i++) {
125
+ const page = await pdf.getPage(i);
126
+ const content = await page.getTextContent();
127
+ const pageText = content.items
128
+ .filter((item: any) => item.str && typeof item.str === 'string')
129
+ .map((item: any) => item.str)
130
+ .join(' ');
131
+ text += pageText + '\n';
132
+ }
133
+
134
+ return text;
135
+ } catch (error) {
136
+ throw new Error(`PDF extraction failed: ${error instanceof Error ? error.message : 'Unknown error'}`);
137
+ }
138
+ };
139
+
140
+ const extractTextFromXLSX = async (file: File): Promise<string> => {
141
+ try {
142
+ if (!window.XLSX) {
143
+ await new Promise((resolve, reject) => {
144
+ const script = document.createElement('script');
145
+ script.src = XLSX_URL;
146
+ script.onload = resolve;
147
+ script.onerror = reject;
148
+ document.head.appendChild(script);
149
+ });
150
+ }
151
+
152
+ const arrayBuffer = await file.arrayBuffer();
153
+ const workbook = window.XLSX.read(arrayBuffer, {
154
+ type: 'array',
155
+ cellFormula: true,
156
+ cellNF: true,
157
+ cellText: true,
158
+ cellDates: true,
159
+ });
160
+
161
+ let text = '';
162
+ for (const sheetName of workbook.SheetNames) {
163
+ const worksheet = workbook.Sheets[sheetName];
164
+ const data = window.XLSX.utils.sheet_to_json(worksheet, { header: 1, raw: false });
165
+
166
+ const colWidths = data.reduce((widths: number[], row: any[]) => {
167
+ row.forEach((cell, i) => {
168
+ const cellWidth = (cell || '').toString().length;
169
+ widths[i] = Math.max(widths[i] || 0, cellWidth);
170
+ });
171
+ return widths;
172
+ }, []);
173
+
174
+ const formattedText = data.map((row: any[]) => {
175
+ return row
176
+ .map((cell, i) => {
177
+ const cellStr = (cell || '').toString();
178
+ return cellStr.padEnd(colWidths[i] + 2);
179
+ })
180
+ .join('|')
181
+ .trim();
182
+ });
183
+
184
+ if (formattedText.length > 0) {
185
+ const separator = colWidths
186
+ .map((w: number) => '-'.repeat(w + 2))
187
+ .join('+');
188
+ formattedText.splice(1, 0, separator);
189
+ }
190
+
191
+ text += `Sheet: ${sheetName}\n${formattedText.join('\n')}\n\n`;
192
+ }
193
+
194
+ return text;
195
+ } catch (error) {
196
+ throw new Error(`XLSX extraction failed: ${error instanceof Error ? error.message : 'Unknown error'}`);
197
+ }
198
+ };
199
+
200
+ const processDocumentFile = async (file: File): Promise<string | null> => {
201
+ const fileExt = file.name.split('.').pop()?.toLowerCase() || '';
202
+
203
+ try {
204
+ let text: string | null = null;
205
+
206
+ if (fileExt === 'pdf') {
207
+ text = await extractTextFromPDF(file);
208
+ } else if (['txt', 'md', 'json', 'csv'].includes(fileExt)) {
209
+ text = await file.text();
210
+ } else if (fileExt === 'xlsx') {
211
+ text = await extractTextFromXLSX(file);
212
+ }
213
+
214
+ if (text && text.length > MAX_TEXT_LENGTH) {
215
+ addError({
216
+ message: `File "${file.name}" content exceeds ${MAX_TEXT_LENGTH} characters and was truncated`,
217
+ severity: 'warning',
218
+ fileId: file.name,
219
+ });
220
+ text = text.substring(0, MAX_TEXT_LENGTH) + "\n\n[Content truncated due to size limits]";
221
+ }
222
+
223
+ return text;
224
+ } catch (error) {
225
+ throw new Error(`Failed to process "${file.name}": ${error instanceof Error ? error.message : 'Unknown error'}`);
226
+ }
227
+ };
228
+
229
+ const handleDocumentUpload = async (e: React.ChangeEvent<HTMLInputElement>) => {
230
+ const files = Array.from(e.target.files || []);
231
+ if (files.length === 0) return;
232
+
233
+ // Only process the first file
234
+ const file = files[0];
235
+
236
+ setIsLoading(true);
237
+ clearErrors();
238
+
239
+ if (!validateDocumentFile(file)) {
240
+ setIsLoading(false);
241
+ if (documentInputRef.current) {
242
+ documentInputRef.current.value = '';
243
+ }
244
+ return;
245
+ }
246
+
247
+ const fileId = Math.random().toString(36).substr(2, 9);
248
+
249
+ try {
250
+ const text = await processDocumentFile(file);
251
+
252
+ if (text) {
253
+
254
+ // Replace document preview files with the new one
255
+ setDocumentPreviewFiles([{
256
+ name: file.name,
257
+ id: fileId,
258
+ content: text,
259
+ mimeType: file.type,
260
+ }]);
261
+ }
262
+ } catch (error) {
263
+ addError({
264
+ message: `${error instanceof Error ? error.message : 'Unknown error'}`,
265
+ severity: 'error',
266
+ fileId: file.name,
267
+ });
268
+ }
269
+
270
+ setIsLoading(false);
271
+ if (documentInputRef.current) {
272
+ documentInputRef.current.value = '';
273
+ }
274
+ };
275
+
276
+ return (
277
+ <div className="memori--document-upload-wrapper">
278
+ {/* Hidden file input */}
279
+ <input
280
+ ref={documentInputRef}
281
+ type="file"
282
+ accept=".pdf,.txt,.md,.json,.xlsx,.csv"
283
+ className="memori--upload-file-input"
284
+ onChange={handleDocumentUpload}
285
+ />
286
+
287
+ {/* Upload document button */}
288
+ <button
289
+ className={cx(
290
+ 'memori-button',
291
+ 'memori-button--circle',
292
+ 'memori-button--icon-only',
293
+ 'memori-share-button--button',
294
+ 'memori--conversation-button',
295
+ 'memori--document-upload-button',
296
+ { 'memori--error': errors.length > 0 }
297
+ )}
298
+ onClick={() => documentInputRef.current?.click()}
299
+ disabled={isLoading || (maxDocuments && documentPreviewFiles.filter((file: any) => file.type !== 'image').length >= maxDocuments) || false}
300
+ title="Upload documents"
301
+ >
302
+ {isLoading ? (
303
+ <Spin spinning className="memori--upload-icon" />
304
+ ) : (
305
+ <React.Fragment>
306
+ <DocumentIcon className="memori--upload-icon" />
307
+ </React.Fragment>
308
+ )}
309
+ </button>
310
+
311
+ {/* Modal */}
312
+ <Modal
313
+ width="80%"
314
+ widthMd="80%"
315
+ open={!!selectedFile}
316
+ className="memori--modal-preview-file"
317
+ onClose={() => setSelectedFile(null)}
318
+ closable
319
+ title={selectedFile?.name}
320
+ >
321
+ <div
322
+ className="memori--preview-content"
323
+ style={{
324
+ maxHeight: '70vh',
325
+ overflowY: 'auto',
326
+ textAlign: 'center',
327
+ whiteSpace: 'pre-wrap'
328
+ }}
329
+ >
330
+ {selectedFile?.content}
331
+ </div>
332
+ </Modal>
333
+
334
+ {/* Error messages container */}
335
+ <div className="memori--error-message-container">
336
+ {errors.map((error, index) => (
337
+ <Alert
338
+ key={`${error.message}-${index}`}
339
+ open={true}
340
+ type={error.severity}
341
+ title={'Upload notification'}
342
+ description={error.message}
343
+ onClose={() => removeError(error.message)}
344
+ width="350px"
345
+ />
346
+ ))}
347
+ </div>
348
+ </div>
349
+ );
350
+ };
351
+
352
+ export default UploadDocuments;