@memori.ai/memori-react 8.42.0 → 8.43.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 (105) hide show
  1. package/CHANGELOG.md +18 -0
  2. package/dist/components/Chat/Chat.js +1 -1
  3. package/dist/components/Chat/Chat.js.map +1 -1
  4. package/dist/components/ChatHistoryDrawer/ChatResumeDrawer.css +6 -1
  5. package/dist/components/ChatInputs/ChatInputs.d.ts +1 -0
  6. package/dist/components/ChatInputs/ChatInputs.js +2 -2
  7. package/dist/components/ChatInputs/ChatInputs.js.map +1 -1
  8. package/dist/components/IconButton/IconButton.css +144 -0
  9. package/dist/components/IconButton/IconButton.d.ts +9 -0
  10. package/dist/components/IconButton/IconButton.js +36 -0
  11. package/dist/components/IconButton/IconButton.js.map +1 -0
  12. package/dist/components/MemoriArtifactSystem/components/ArtifactPreview/ArtifactPreview.js +1 -1
  13. package/dist/components/MemoriArtifactSystem/components/ArtifactPreview/ArtifactPreview.js.map +1 -1
  14. package/dist/components/MobileSessionPanel/MobileSessionPanel.css +29 -7
  15. package/dist/components/MobileSessionPanel/MobileSessionPanel.d.ts +3 -0
  16. package/dist/components/MobileSessionPanel/MobileSessionPanel.js +8 -6
  17. package/dist/components/MobileSessionPanel/MobileSessionPanel.js.map +1 -1
  18. package/dist/components/PositionPopover/PositionPopover.d.ts +2 -0
  19. package/dist/components/PositionPopover/PositionPopover.js +19 -11
  20. package/dist/components/PositionPopover/PositionPopover.js.map +1 -1
  21. package/dist/components/UploadButton/UploadButton.d.ts +1 -0
  22. package/dist/components/UploadButton/UploadButton.js +12 -18
  23. package/dist/components/UploadButton/UploadButton.js.map +1 -1
  24. package/dist/components/UploadButton/UploadDocuments/UploadDocuments.d.ts +1 -6
  25. package/dist/components/UploadButton/UploadDocuments/UploadDocuments.js +7 -139
  26. package/dist/components/UploadButton/UploadDocuments/UploadDocuments.js.map +1 -1
  27. package/dist/components/icons/Print.js +1 -1
  28. package/dist/components/icons/Print.js.map +1 -1
  29. package/dist/components/layouts/WebsiteAssistant/WebsiteAssistant.js +24 -3
  30. package/dist/components/layouts/WebsiteAssistant/WebsiteAssistant.js.map +1 -1
  31. package/dist/components/layouts/WebsiteAssistant/website-assistant.css +137 -55
  32. package/dist/components/layouts/fullpage.css +165 -68
  33. package/dist/helpers/constants.d.ts +1 -0
  34. package/dist/helpers/constants.js +24 -1
  35. package/dist/helpers/constants.js.map +1 -1
  36. package/dist/helpers/convertDocument.d.ts +1 -0
  37. package/dist/helpers/convertDocument.js +24 -0
  38. package/dist/helpers/convertDocument.js.map +1 -0
  39. package/dist/helpers/positionPopover.d.ts +1 -1
  40. package/dist/helpers/positionPopover.js +2 -2
  41. package/dist/helpers/positionPopover.js.map +1 -1
  42. package/dist/helpers/tts/muteSpeaker.d.ts +13 -0
  43. package/dist/helpers/tts/muteSpeaker.js +26 -0
  44. package/dist/helpers/tts/muteSpeaker.js.map +1 -0
  45. package/dist/version.d.ts +1 -1
  46. package/dist/version.js +1 -1
  47. package/esm/components/Chat/Chat.js +1 -1
  48. package/esm/components/Chat/Chat.js.map +1 -1
  49. package/esm/components/ChatHistoryDrawer/ChatResumeDrawer.css +6 -1
  50. package/esm/components/ChatInputs/ChatInputs.d.ts +1 -0
  51. package/esm/components/ChatInputs/ChatInputs.js +2 -2
  52. package/esm/components/ChatInputs/ChatInputs.js.map +1 -1
  53. package/esm/components/IconButton/IconButton.css +144 -0
  54. package/esm/components/IconButton/IconButton.d.ts +9 -0
  55. package/esm/components/IconButton/IconButton.js +33 -0
  56. package/esm/components/IconButton/IconButton.js.map +1 -0
  57. package/esm/components/MemoriArtifactSystem/components/ArtifactPreview/ArtifactPreview.js +2 -2
  58. package/esm/components/MemoriArtifactSystem/components/ArtifactPreview/ArtifactPreview.js.map +1 -1
  59. package/esm/components/MobileSessionPanel/MobileSessionPanel.css +29 -7
  60. package/esm/components/MobileSessionPanel/MobileSessionPanel.d.ts +3 -0
  61. package/esm/components/MobileSessionPanel/MobileSessionPanel.js +8 -6
  62. package/esm/components/MobileSessionPanel/MobileSessionPanel.js.map +1 -1
  63. package/esm/components/PositionPopover/PositionPopover.d.ts +2 -0
  64. package/esm/components/PositionPopover/PositionPopover.js +20 -12
  65. package/esm/components/PositionPopover/PositionPopover.js.map +1 -1
  66. package/esm/components/UploadButton/UploadButton.d.ts +1 -0
  67. package/esm/components/UploadButton/UploadButton.js +13 -19
  68. package/esm/components/UploadButton/UploadButton.js.map +1 -1
  69. package/esm/components/UploadButton/UploadDocuments/UploadDocuments.d.ts +1 -6
  70. package/esm/components/UploadButton/UploadDocuments/UploadDocuments.js +8 -140
  71. package/esm/components/UploadButton/UploadDocuments/UploadDocuments.js.map +1 -1
  72. package/esm/components/icons/Print.js +1 -1
  73. package/esm/components/icons/Print.js.map +1 -1
  74. package/esm/components/layouts/WebsiteAssistant/WebsiteAssistant.js +24 -3
  75. package/esm/components/layouts/WebsiteAssistant/WebsiteAssistant.js.map +1 -1
  76. package/esm/components/layouts/WebsiteAssistant/website-assistant.css +137 -55
  77. package/esm/components/layouts/fullpage.css +165 -68
  78. package/esm/helpers/constants.d.ts +1 -0
  79. package/esm/helpers/constants.js +23 -0
  80. package/esm/helpers/constants.js.map +1 -1
  81. package/esm/helpers/convertDocument.d.ts +1 -0
  82. package/esm/helpers/convertDocument.js +20 -0
  83. package/esm/helpers/convertDocument.js.map +1 -0
  84. package/esm/helpers/positionPopover.d.ts +1 -1
  85. package/esm/helpers/positionPopover.js +2 -2
  86. package/esm/helpers/positionPopover.js.map +1 -1
  87. package/esm/helpers/tts/muteSpeaker.d.ts +13 -0
  88. package/esm/helpers/tts/muteSpeaker.js +21 -0
  89. package/esm/helpers/tts/muteSpeaker.js.map +1 -0
  90. package/esm/version.d.ts +1 -1
  91. package/esm/version.js +1 -1
  92. package/package.json +2 -2
  93. package/src/components/Chat/Chat.tsx +1 -0
  94. package/src/components/ChatInputs/ChatInputs.tsx +3 -0
  95. package/src/components/FilePreview/FilePreview.stories.tsx +0 -3
  96. package/src/components/MemoriArtifactSystem/components/ArtifactPreview/ArtifactPreview.tsx +2 -2
  97. package/src/components/UploadButton/UploadButton.stories.tsx +2 -2
  98. package/src/components/UploadButton/UploadButton.tsx +19 -19
  99. package/src/components/UploadButton/UploadDocuments/UploadDocuments.tsx +13 -187
  100. package/src/components/UploadButton/__snapshots__/UploadButton.test.tsx.snap +2 -2
  101. package/src/components/icons/Print.tsx +6 -2
  102. package/src/helpers/constants.ts +24 -0
  103. package/src/helpers/convertDocument.test.ts +67 -0
  104. package/src/helpers/convertDocument.ts +35 -0
  105. package/src/version.ts +1 -1
@@ -121,7 +121,7 @@ A unified upload button that supports both images and documents with the followi
121
121
  ## Limits
122
122
 
123
123
  - **Images**: Maximum 5 images, 15MB per file, formats: .jpg, .jpeg, .png
124
- - **Documents**: Maximum 5 documents, 15MB per file, formats: .pdf, .txt, .json, .xlsx, .csv, .md
124
+ - **Documents**: Maximum 5 documents, 25MB per file; PDF, Office, OpenDocument, HTML, structured text, and plain-text formats
125
125
  - **Content**: Document content is limited to 300,000 characters per document (truncated if exceeded)
126
126
  `,
127
127
  },
@@ -206,7 +206,7 @@ const Template: Story<UploadButtonProps> = (args) => {
206
206
  <li>Drag and drop files anywhere in this area</li>
207
207
  <li>Press <kbd style={{ padding: '2px 6px', backgroundColor: '#f0f0f0', borderRadius: '3px' }}>Cmd+V</kbd> / <kbd style={{ padding: '2px 6px', backgroundColor: '#f0f0f0', borderRadius: '3px' }}>Ctrl+V</kbd> to open file chooser</li>
208
208
  <li>Paste files from clipboard using <kbd style={{ padding: '2px 6px', backgroundColor: '#f0f0f0', borderRadius: '3px' }}>Cmd+V</kbd> / <kbd style={{ padding: '2px 6px', backgroundColor: '#f0f0f0', borderRadius: '3px' }}>Ctrl+V</kbd></li>
209
- <li>Upload both images (.jpg, .jpeg, .png) and documents (.pdf, .txt, .json, .xlsx, .csv, .md)</li>
209
+ <li>Upload images and supported PDF, Office, OpenDocument, HTML, or text documents</li>
210
210
  </ul>
211
211
  </div>
212
212
  </div>
@@ -9,13 +9,17 @@ import UploadDocuments from './UploadDocuments/UploadDocuments';
9
9
  import UploadImages from './UploadImages/UploadImages';
10
10
  import { useTranslation } from 'react-i18next';
11
11
  import memoriApiClient from '@memori.ai/memori-api-client';
12
- import { officeNativeExtensions } from '../../helpers/constants';
12
+ import {
13
+ documentConversionExtensions,
14
+ officeNativeExtensions,
15
+ } from '../../helpers/constants';
13
16
  import Tooltip from '../ui/Tooltip';
14
17
  // Props interface
15
18
  interface UploadManagerProps {
16
19
  authToken?: string;
17
20
  client?: ReturnType<typeof memoriApiClient>;
18
21
  sessionID?: string;
22
+ baseUrl?: string;
19
23
  isMediaAccepted?: boolean;
20
24
  setDocumentPreviewFiles: any;
21
25
  documentPreviewFiles: {
@@ -40,6 +44,7 @@ const UploadButton: React.FC<UploadManagerProps> = ({
40
44
  authToken = '',
41
45
  client,
42
46
  sessionID = '',
47
+ baseUrl = '',
43
48
  isMediaAccepted = false,
44
49
  setDocumentPreviewFiles,
45
50
  documentPreviewFiles,
@@ -101,14 +106,8 @@ const UploadButton: React.FC<UploadManagerProps> = ({
101
106
 
102
107
  // Check if file is a document
103
108
  const isDocumentFile = (file: File): boolean => {
104
- const documentExtensions = [
105
- '.pdf',
106
- '.txt',
107
- '.json',
108
- '.xlsx',
109
- '.csv',
110
- '.md',
111
- '.html',
109
+ const documentExtensions: readonly string[] = [
110
+ ...documentConversionExtensions,
112
111
  ...officeNativeExtensions,
113
112
  ];
114
113
  const fileExt = `.${file.name.split('.').pop()?.toLowerCase()}`;
@@ -453,17 +452,11 @@ ${file.textAssetUrl || ''}
453
452
  // Document validation and error handling
454
453
  const validateDocumentFile = (file: File): boolean => {
455
454
  const fileExt = `.${file.name.split('.').pop()?.toLowerCase()}`;
456
- const ALLOWED_FILE_TYPES = [
457
- '.pdf',
458
- '.txt',
459
- '.json',
460
- '.xlsx',
461
- '.csv',
462
- '.md',
463
- '.html',
455
+ const ALLOWED_FILE_TYPES: readonly string[] = [
456
+ ...documentConversionExtensions,
464
457
  ...officeNativeExtensions,
465
458
  ];
466
- const MAX_FILE_SIZE = 15 * 1024 * 1024; // 15MB
459
+ const MAX_FILE_SIZE = 25 * 1024 * 1024;
467
460
 
468
461
  if (!ALLOWED_FILE_TYPES.includes(fileExt)) {
469
462
  addError({
@@ -578,7 +571,13 @@ ${file.textAssetUrl || ''}
578
571
  <input
579
572
  ref={unifiedInputRef}
580
573
  type="file"
581
- accept={`.jpg,.jpeg,.png,.pdf,.txt,.json,.xlsx,.csv,.md,.html,${officeNativeExtensions.join(',')}`}
574
+ accept={[
575
+ '.jpg',
576
+ '.jpeg',
577
+ '.png',
578
+ ...documentConversionExtensions,
579
+ ...officeNativeExtensions,
580
+ ].join(',')}
582
581
  multiple
583
582
  className="memori--upload-file-input"
584
583
  onChange={handleFileInputChange}
@@ -634,6 +633,7 @@ ${file.textAssetUrl || ''}
634
633
  authToken={authToken}
635
634
  client={client}
636
635
  sessionID={sessionID}
636
+ baseUrl={baseUrl}
637
637
  memoriID={memoriID}
638
638
  maxDocuments={maxDocumentsPerMessage}
639
639
  documentPreviewFiles={documentPreviewFiles}
@@ -5,8 +5,12 @@ import { DocumentIcon } from '../../icons/Document';
5
5
  import Modal from '../../ui/Modal';
6
6
  import { useTranslation } from 'react-i18next';
7
7
  import memoriApiClient from '@memori.ai/memori-api-client';
8
- import { officeNativeExtensions } from '../../../helpers/constants';
8
+ import {
9
+ documentConversionExtensions,
10
+ officeNativeExtensions,
11
+ } from '../../../helpers/constants';
9
12
  import { isOfficeNativeFilename } from '../../../helpers/utils';
13
+ import { convertDocument } from '../../../helpers/convertDocument';
10
14
  // Types
11
15
  type PreviewFile = {
12
16
  name: string;
@@ -18,21 +22,6 @@ type PreviewFile = {
18
22
  error?: boolean;
19
23
  };
20
24
 
21
- // Constants
22
- const PDF_JS_VERSION = '3.11.174';
23
- const WORKER_URL = `https://cdnjs.cloudflare.com/ajax/libs/pdf.js/${PDF_JS_VERSION}/pdf.worker.min.js`;
24
- const PDF_JS_URL = `https://cdnjs.cloudflare.com/ajax/libs/pdf.js/${PDF_JS_VERSION}/pdf.min.js`;
25
- const XLSX_URL =
26
- 'https://cdn.sheetjs.com/xlsx-0.20.0/package/dist/xlsx.full.min.js';
27
-
28
- // Add type definitions for external libraries
29
- declare global {
30
- interface Window {
31
- pdfjsLib: any;
32
- XLSX: any;
33
- }
34
- }
35
-
36
25
  // Props interface
37
26
  interface UploadDocumentsProps {
38
27
  setDocumentPreviewFiles: (
@@ -47,6 +36,7 @@ interface UploadDocumentsProps {
47
36
  authToken?: string;
48
37
  client?: ReturnType<typeof memoriApiClient>;
49
38
  sessionID?: string;
39
+ baseUrl?: string;
50
40
  memoriID?: string;
51
41
  maxDocuments?: number;
52
42
  documentPreviewFiles: any;
@@ -71,6 +61,7 @@ const UploadDocuments: React.FC<UploadDocumentsProps> = ({
71
61
  authToken = '',
72
62
  client,
73
63
  sessionID = '',
64
+ baseUrl = '',
74
65
  memoriID = '',
75
66
  maxDocuments,
76
67
  documentPreviewFiles,
@@ -123,182 +114,19 @@ const UploadDocuments: React.FC<UploadDocumentsProps> = ({
123
114
  return { valid: true };
124
115
  };
125
116
 
126
- const extractTextFromPDF = async (file: File): Promise<string> => {
127
- try {
128
- // Load PDF.js if not already loaded
129
- if (!window.pdfjsLib) {
130
- await new Promise((resolve, reject) => {
131
- const script = document.createElement('script');
132
- script.src = PDF_JS_URL;
133
- script.onload = () => {
134
- window.pdfjsLib.GlobalWorkerOptions.workerSrc = WORKER_URL;
135
- resolve(true);
136
- };
137
- script.onerror = reject;
138
- document.head.appendChild(script);
139
- });
140
- }
141
-
142
- // Extract text from PDF
143
- const arrayBuffer = await file.arrayBuffer();
144
- const pdf = await window.pdfjsLib.getDocument({ data: arrayBuffer })
145
- .promise;
146
- let text = '';
147
-
148
- // Iterate through each page and extract text
149
- for (let i = 1; i <= pdf.numPages; i++) {
150
- const page = await pdf.getPage(i);
151
- const content = await page.getTextContent();
152
- const pageText = content.items
153
- .filter((item: any) => item.str && typeof item.str === 'string')
154
- .map((item: any) => item.str)
155
- .join(' ');
156
- text += pageText + '\n';
157
- }
158
-
159
- return text;
160
- } catch (error) {
161
- console.error('PDF extraction failed:', error);
162
- throw new Error(
163
- `PDF extraction failed: ${
164
- error instanceof Error ? error.message : 'Unknown error'
165
- }`
166
- );
167
- }
168
- };
169
-
170
- const loadXLSXLibrary = async (): Promise<void> => {
171
- if (window.XLSX) return;
172
-
173
- await new Promise((resolve, reject) => {
174
- const script = document.createElement('script');
175
- script.src = XLSX_URL;
176
- script.onload = resolve;
177
- script.onerror = reject;
178
- document.head.appendChild(script);
179
- });
180
- };
181
-
182
- const readXLSXWorkbook = async (file: File) => {
183
- await loadXLSXLibrary();
184
-
185
- const arrayBuffer = await file.arrayBuffer();
186
- return window.XLSX.read(arrayBuffer, {
187
- type: 'array',
188
- cellFormula: true,
189
- cellNF: true,
190
- cellText: true,
191
- cellDates: true,
192
- });
193
- };
194
-
195
- const formatXLSXWorkbookForDisplay = (workbook: {
196
- SheetNames: string[];
197
- Sheets: Record<string, unknown>;
198
- }): string => {
199
- let text = '';
200
-
201
- for (const sheetName of workbook.SheetNames) {
202
- const worksheet = workbook.Sheets[sheetName];
203
- const data = window.XLSX.utils.sheet_to_json(worksheet, {
204
- header: 1,
205
- raw: false,
206
- });
207
-
208
- const colWidths = data.reduce((widths: number[], row: any[]) => {
209
- row.forEach((cell, i) => {
210
- const cellWidth = (cell || '').toString().length;
211
- widths[i] = Math.max(widths[i] || 0, cellWidth);
212
- });
213
- return widths;
214
- }, []);
215
-
216
- const formattedText = data.map((row: any[]) => {
217
- return row
218
- .map((cell, i) => {
219
- const cellStr = (cell || '').toString();
220
- return cellStr.padEnd(colWidths[i] + 2);
221
- })
222
- .join('|')
223
- .trim();
224
- });
225
-
226
- if (formattedText.length > 0) {
227
- const separator = colWidths
228
- .map((w: number) => '-'.repeat(w + 2))
229
- .join('+');
230
- formattedText.splice(1, 0, separator);
231
- }
232
-
233
- text += `Sheet: ${sheetName}\n${formattedText.join('\n')}\n\n`;
234
- }
235
-
236
- return text;
237
- };
238
-
239
- const formatXLSXWorkbookAsCsv = (workbook: {
240
- SheetNames: string[];
241
- Sheets: Record<string, unknown>;
242
- }): string => {
243
- let text = '';
244
-
245
- for (const sheetName of workbook.SheetNames) {
246
- const worksheet = workbook.Sheets[sheetName];
247
- const csv = window.XLSX.utils.sheet_to_csv(worksheet, { raw: false });
248
- if (csv.trim()) {
249
- text += `Sheet: ${sheetName}\n${csv}\n\n`;
250
- }
251
- }
252
-
253
- return text;
254
- };
255
-
256
- const extractTextFromXLSX = async (
257
- file: File
258
- ): Promise<{ display: string; asset: string }> => {
259
- try {
260
- const workbook = await readXLSXWorkbook(file);
261
- return {
262
- display: formatXLSXWorkbookForDisplay(workbook),
263
- asset: formatXLSXWorkbookAsCsv(workbook),
264
- };
265
- } catch (error) {
266
- console.error('XLSX extraction failed:', error);
267
- throw new Error(
268
- `XLSX extraction failed: ${
269
- error instanceof Error ? error.message : 'Unknown error'
270
- }`
271
- );
272
- }
273
- };
274
-
275
117
  const processDocumentFile = async (
276
118
  file: File
277
119
  ): Promise<{
278
120
  text: string | null;
279
- textForAsset?: string;
280
121
  uploadAsOriginal?: boolean;
281
122
  }> => {
282
123
  if (isOfficeNativeFilename(file.name)) {
283
124
  return { text: null, uploadAsOriginal: true };
284
125
  }
285
126
 
286
- const ext = file.name.split('.').pop()?.toLowerCase() || '';
287
127
  try {
288
- let text: string | null = null;
289
- let textForAsset: string | undefined;
290
-
291
- if (ext === 'pdf') {
292
- text = await extractTextFromPDF(file);
293
- } else if (['txt', 'md', 'json', 'csv', 'html'].includes(ext)) {
294
- text = await file.text();
295
- } else if (ext === 'xlsx') {
296
- const extracted = await extractTextFromXLSX(file);
297
- text = extracted.display;
298
- textForAsset = extracted.asset;
299
- }
300
-
301
- return { text, textForAsset };
128
+ const text = await convertDocument(file, sessionID, baseUrl);
129
+ return { text };
302
130
  } catch (error) {
303
131
  console.error('Document processing failed:', error);
304
132
  throw new Error(
@@ -414,8 +242,7 @@ const UploadDocuments: React.FC<UploadDocumentsProps> = ({
414
242
  const fileId = Math.random().toString(36).substr(2, 9);
415
243
 
416
244
  try {
417
- const { text, textForAsset, uploadAsOriginal } =
418
- await processDocumentFile(file);
245
+ const { text, uploadAsOriginal } = await processDocumentFile(file);
419
246
 
420
247
  if (uploadAsOriginal) {
421
248
  // Office native format: upload the original file as asset, no text extraction
@@ -448,8 +275,7 @@ const UploadDocuments: React.FC<UploadDocumentsProps> = ({
448
275
  });
449
276
  } else if (text) {
450
277
  const baseName = file.name.replace(/\.[^/.]+$/, '') || file.name;
451
- const assetText = textForAsset ?? text;
452
- const textFile = new File([assetText], `${baseName}.txt`, {
278
+ const textFile = new File([text], `${baseName}.txt`, {
453
279
  type: 'text/plain',
454
280
  });
455
281
 
@@ -514,9 +340,9 @@ const UploadDocuments: React.FC<UploadDocumentsProps> = ({
514
340
  <input
515
341
  ref={documentInputRef}
516
342
  type="file"
517
- accept={`.pdf,.txt,.md,.json,.xlsx,.csv,.html,${officeNativeExtensions.join(
343
+ accept={[...documentConversionExtensions, ...officeNativeExtensions].join(
518
344
  ','
519
- )}`}
345
+ )}
520
346
  multiple
521
347
  className="memori--upload-file-input"
522
348
  onChange={handleDocumentUpload}
@@ -6,7 +6,7 @@ exports[`renders UploadButton unchanged 1`] = `
6
6
  class="memori--unified-upload-wrapper"
7
7
  >
8
8
  <input
9
- accept=".jpg,.jpeg,.png,.pdf,.txt,.json,.xlsx,.csv,.md,.html,.doc,.docx,.dotx,.xltx,.potx"
9
+ accept=".jpg,.jpeg,.png,.pdf,.docm,.xlsx,.xlsm,.xls,.ods,.pptx,.pptm,.html,.htm,.xhtml,.csv,.tsv,.txt,.md,.markdown,.json,.xml,.log,.yml,.yaml,.doc,.docx,.dotx,.xltx,.potx"
10
10
  class="memori--upload-file-input"
11
11
  multiple=""
12
12
  style="display: none;"
@@ -52,7 +52,7 @@ exports[`renders UploadButton unchanged 1`] = `
52
52
  class="memori--document-upload-wrapper"
53
53
  >
54
54
  <input
55
- accept=".pdf,.txt,.md,.json,.xlsx,.csv,.html,.doc,.docx,.dotx,.xltx,.potx"
55
+ accept=".pdf,.docm,.xlsx,.xlsm,.xls,.ods,.pptx,.pptm,.html,.htm,.xhtml,.csv,.tsv,.txt,.md,.markdown,.json,.xml,.log,.yml,.yaml,.doc,.docx,.dotx,.xltx,.potx"
56
56
  class="memori--upload-file-input"
57
57
  multiple=""
58
58
  type="file"
@@ -1,5 +1,9 @@
1
1
  import * as React from 'react';
2
- const PrintIcon: React.FC<{ className?: string, title?: string }> = ({ className, title }) => (
2
+
3
+ const PrintIcon: React.FC<{ className?: string; title?: string }> = ({
4
+ className,
5
+ title,
6
+ }) => (
3
7
  <svg
4
8
  {...(!title ? { 'aria-hidden': 'true' } : {})}
5
9
  aria-label={title}
@@ -14,7 +18,7 @@ const PrintIcon: React.FC<{ className?: string, title?: string }> = ({ className
14
18
  <g
15
19
  id="Page-1"
16
20
  stroke="none"
17
- stroke-width="1"
21
+ strokeWidth="1"
18
22
  fill="none"
19
23
  fill-rule="evenodd"
20
24
  >
@@ -52,6 +52,30 @@ export const officeNativeExtensions = [
52
52
  '.potx',
53
53
  ] as const;
54
54
 
55
+ export const documentConversionExtensions = [
56
+ '.pdf',
57
+ '.docm',
58
+ '.xlsx',
59
+ '.xlsm',
60
+ '.xls',
61
+ '.ods',
62
+ '.pptx',
63
+ '.pptm',
64
+ '.html',
65
+ '.htm',
66
+ '.xhtml',
67
+ '.csv',
68
+ '.tsv',
69
+ '.txt',
70
+ '.md',
71
+ '.markdown',
72
+ '.json',
73
+ '.xml',
74
+ '.log',
75
+ '.yml',
76
+ '.yaml',
77
+ ] as const;
78
+
55
79
  export const allowedMediaTypes = [
56
80
  'image/jpeg',
57
81
  'image/png',
@@ -0,0 +1,67 @@
1
+ import { convertDocument } from './convertDocument';
2
+
3
+ describe('convertDocument', () => {
4
+ const fetchMock = jest.fn();
5
+
6
+ beforeEach(() => {
7
+ fetchMock.mockReset();
8
+ globalThis.fetch = fetchMock as unknown as typeof fetch;
9
+ });
10
+
11
+ it('uploads a file through the session-gated endpoint', async () => {
12
+ fetchMock.mockResolvedValue({
13
+ ok: true,
14
+ json: jest.fn().mockResolvedValue({ text: '# Converted document' }),
15
+ });
16
+ const file = new File(['document'], 'contract.pdf', {
17
+ type: 'application/pdf',
18
+ });
19
+
20
+ await expect(
21
+ convertDocument(file, 'session/id', 'https://www.aisuru.com/')
22
+ ).resolves.toBe('# Converted document');
23
+
24
+ expect(fetchMock).toHaveBeenCalledWith(
25
+ 'https://www.aisuru.com/api/convert/session%2Fid',
26
+ expect.objectContaining({
27
+ method: 'POST',
28
+ body: expect.any(FormData),
29
+ })
30
+ );
31
+ const body = fetchMock.mock.calls[0][1].body as FormData;
32
+ expect(body.get('file')).toBe(file);
33
+ expect(fetchMock.mock.calls[0][1].headers).toBeUndefined();
34
+ });
35
+
36
+ it('uses the current origin when baseUrl is omitted', async () => {
37
+ fetchMock.mockResolvedValue({
38
+ ok: true,
39
+ json: jest.fn().mockResolvedValue({ text: 'Converted' }),
40
+ });
41
+
42
+ await convertDocument(new File(['text'], 'notes.txt'), 'session');
43
+
44
+ expect(fetchMock).toHaveBeenCalledWith(
45
+ '/api/convert/session',
46
+ expect.any(Object)
47
+ );
48
+ });
49
+
50
+ it('surfaces conversion API errors', async () => {
51
+ fetchMock.mockResolvedValue({
52
+ ok: false,
53
+ json: jest.fn().mockResolvedValue({ error: 'Formato non supportato' }),
54
+ });
55
+
56
+ await expect(
57
+ convertDocument(new File(['document'], 'contract.pages'), 'session')
58
+ ).rejects.toThrow('Formato non supportato');
59
+ });
60
+
61
+ it('requires a session before uploading', async () => {
62
+ await expect(
63
+ convertDocument(new File(['document'], 'contract.pdf'), '')
64
+ ).rejects.toThrow('Session ID is required');
65
+ expect(fetchMock).not.toHaveBeenCalled();
66
+ });
67
+ });
@@ -0,0 +1,35 @@
1
+ type ConvertDocumentResponse = {
2
+ text?: string;
3
+ error?: string;
4
+ };
5
+
6
+ export const convertDocument = async (
7
+ file: File,
8
+ sessionID: string,
9
+ baseUrl = ''
10
+ ): Promise<string> => {
11
+ if (!sessionID) {
12
+ throw new Error('Session ID is required to convert documents');
13
+ }
14
+
15
+ const body = new FormData();
16
+ body.append('file', file);
17
+
18
+ const origin = baseUrl.replace(/\/+$/, '');
19
+ const response = await fetch(
20
+ `${origin}/api/convert/${encodeURIComponent(sessionID)}`,
21
+ {
22
+ method: 'POST',
23
+ body,
24
+ }
25
+ );
26
+ const payload = (await response
27
+ .json()
28
+ .catch(() => null)) as ConvertDocumentResponse | null;
29
+
30
+ if (!response.ok || !payload?.text) {
31
+ throw new Error(payload?.error || 'Document conversion failed');
32
+ }
33
+
34
+ return payload.text;
35
+ };
package/src/version.ts CHANGED
@@ -1,2 +1,2 @@
1
1
  // This file is auto-generated. Do not edit manually.
2
- export const version = '8.42.0';
2
+ export const version = '8.43.0';