@memori.ai/memori-react 9.0.0-alpha.5 → 9.0.0-alpha.6
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.
- package/CHANGELOG.md +49 -0
- package/dist/components/AgeVerificationModal/AgeVerificationModal.js +5 -2
- package/dist/components/AgeVerificationModal/AgeVerificationModal.js.map +1 -1
- package/dist/components/Avatar/Avatar.js +5 -10
- package/dist/components/Avatar/Avatar.js.map +1 -1
- package/dist/components/Avatar/AvatarView/AvatarComponent/components/FullbodyAvatar/fullbodyAvatar.js +8 -1
- package/dist/components/Avatar/AvatarView/AvatarComponent/components/FullbodyAvatar/fullbodyAvatar.js.map +1 -1
- package/dist/components/Avatar/AvatarView/AvatarComponent/components/controllers/MorphTargetController.js +1 -1
- package/dist/components/Avatar/AvatarView/AvatarComponent/components/controllers/MorphTargetController.js.map +1 -1
- package/dist/components/Avatar/AvatarView/AvatarComponent/constants.d.ts +2 -0
- package/dist/components/Avatar/AvatarView/AvatarComponent/constants.js +14 -5
- package/dist/components/Avatar/AvatarView/AvatarComponent/constants.js.map +1 -1
- package/dist/components/ChatBubble/ChatBubble.js +19 -15
- package/dist/components/ChatBubble/ChatBubble.js.map +1 -1
- package/dist/components/CompletionProviderStatus/CompletionProviderStatus.js +6 -3
- package/dist/components/CompletionProviderStatus/CompletionProviderStatus.js.map +1 -1
- package/dist/components/DateSelector/DateSelector.d.ts +2 -1
- package/dist/components/DateSelector/DateSelector.js +17 -7
- package/dist/components/DateSelector/DateSelector.js.map +1 -1
- package/dist/components/FilePreview/FilePreview.js +22 -5
- package/dist/components/FilePreview/FilePreview.js.map +1 -1
- package/dist/components/MediaWidget/MediaItemWidget.js +14 -12
- package/dist/components/MediaWidget/MediaItemWidget.js.map +1 -1
- package/dist/components/MediaWidget/MediaItemWidget.utils.d.ts +3 -2
- package/dist/components/MediaWidget/MediaItemWidget.utils.js +28 -6
- package/dist/components/MediaWidget/MediaItemWidget.utils.js.map +1 -1
- package/dist/components/MediaWidget/MediaPreviewModal.js +6 -7
- package/dist/components/MediaWidget/MediaPreviewModal.js.map +1 -1
- package/dist/components/MemoriWidget/MemoriWidget.js +632 -280
- package/dist/components/MemoriWidget/MemoriWidget.js.map +1 -1
- package/dist/components/UploadButton/UploadButton.js +21 -5
- package/dist/components/UploadButton/UploadButton.js.map +1 -1
- package/dist/components/UploadButton/UploadDocuments/UploadDocuments.js +123 -59
- package/dist/components/UploadButton/UploadDocuments/UploadDocuments.js.map +1 -1
- package/dist/components/layouts/WebsiteAssistant/website-assistant.css +2 -1
- package/dist/helpers/constants.d.ts +3 -0
- package/dist/helpers/constants.js +27 -1
- package/dist/helpers/constants.js.map +1 -1
- package/dist/helpers/credits.d.ts +5 -2
- package/dist/helpers/credits.js +5 -1
- package/dist/helpers/credits.js.map +1 -1
- package/dist/helpers/message.d.ts +1 -1
- package/dist/helpers/message.js +4 -4
- package/dist/helpers/message.js.map +1 -1
- package/dist/helpers/nats/getNatsConfig.d.ts +9 -0
- package/dist/helpers/nats/getNatsConfig.js +65 -0
- package/dist/helpers/nats/getNatsConfig.js.map +1 -0
- package/dist/helpers/nats/isSessionExpiredError.d.ts +3 -0
- package/dist/helpers/nats/isSessionExpiredError.js +36 -0
- package/dist/helpers/nats/isSessionExpiredError.js.map +1 -0
- package/dist/helpers/nats/useNats.d.ts +12 -0
- package/dist/helpers/nats/useNats.js +75 -0
- package/dist/helpers/nats/useNats.js.map +1 -0
- package/dist/helpers/nats/useNatsSession.d.ts +28 -0
- package/dist/helpers/nats/useNatsSession.js +212 -0
- package/dist/helpers/nats/useNatsSession.js.map +1 -0
- package/dist/helpers/utils.d.ts +21 -0
- package/dist/helpers/utils.js +75 -4
- package/dist/helpers/utils.js.map +1 -1
- package/dist/locales/de.json +1 -0
- package/dist/locales/en.json +1 -0
- package/dist/locales/es.json +1 -0
- package/dist/locales/fr.json +1 -0
- package/dist/locales/it.json +1 -0
- package/dist/styles.css +1 -1
- package/dist/types/integration.d.ts +3 -0
- package/dist/types/integration.js +16 -0
- package/dist/types/integration.js.map +1 -0
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/esm/components/AgeVerificationModal/AgeVerificationModal.js +5 -2
- package/esm/components/AgeVerificationModal/AgeVerificationModal.js.map +1 -1
- package/esm/components/Avatar/Avatar.js +5 -10
- package/esm/components/Avatar/Avatar.js.map +1 -1
- package/esm/components/Avatar/AvatarView/AvatarComponent/components/FullbodyAvatar/fullbodyAvatar.js +9 -2
- package/esm/components/Avatar/AvatarView/AvatarComponent/components/FullbodyAvatar/fullbodyAvatar.js.map +1 -1
- package/esm/components/Avatar/AvatarView/AvatarComponent/components/controllers/MorphTargetController.js +2 -2
- package/esm/components/Avatar/AvatarView/AvatarComponent/components/controllers/MorphTargetController.js.map +1 -1
- package/esm/components/Avatar/AvatarView/AvatarComponent/constants.d.ts +2 -0
- package/esm/components/Avatar/AvatarView/AvatarComponent/constants.js +13 -4
- package/esm/components/Avatar/AvatarView/AvatarComponent/constants.js.map +1 -1
- package/esm/components/ChatBubble/ChatBubble.js +19 -15
- package/esm/components/ChatBubble/ChatBubble.js.map +1 -1
- package/esm/components/CompletionProviderStatus/CompletionProviderStatus.js +6 -3
- package/esm/components/CompletionProviderStatus/CompletionProviderStatus.js.map +1 -1
- package/esm/components/DateSelector/DateSelector.d.ts +2 -1
- package/esm/components/DateSelector/DateSelector.js +17 -7
- package/esm/components/DateSelector/DateSelector.js.map +1 -1
- package/esm/components/FilePreview/FilePreview.js +24 -7
- package/esm/components/FilePreview/FilePreview.js.map +1 -1
- package/esm/components/MediaWidget/MediaItemWidget.js +16 -14
- package/esm/components/MediaWidget/MediaItemWidget.js.map +1 -1
- package/esm/components/MediaWidget/MediaItemWidget.utils.d.ts +3 -2
- package/esm/components/MediaWidget/MediaItemWidget.utils.js +27 -6
- package/esm/components/MediaWidget/MediaItemWidget.utils.js.map +1 -1
- package/esm/components/MediaWidget/MediaPreviewModal.js +6 -7
- package/esm/components/MediaWidget/MediaPreviewModal.js.map +1 -1
- package/esm/components/MemoriWidget/MemoriWidget.js +632 -280
- package/esm/components/MemoriWidget/MemoriWidget.js.map +1 -1
- package/esm/components/UploadButton/UploadButton.js +21 -5
- package/esm/components/UploadButton/UploadButton.js.map +1 -1
- package/esm/components/UploadButton/UploadDocuments/UploadDocuments.js +123 -59
- package/esm/components/UploadButton/UploadDocuments/UploadDocuments.js.map +1 -1
- package/esm/components/layouts/WebsiteAssistant/website-assistant.css +2 -1
- package/esm/helpers/constants.d.ts +3 -0
- package/esm/helpers/constants.js +26 -0
- package/esm/helpers/constants.js.map +1 -1
- package/esm/helpers/credits.d.ts +5 -2
- package/esm/helpers/credits.js +5 -1
- package/esm/helpers/credits.js.map +1 -1
- package/esm/helpers/message.d.ts +1 -1
- package/esm/helpers/message.js +2 -2
- package/esm/helpers/message.js.map +1 -1
- package/esm/helpers/nats/getNatsConfig.d.ts +9 -0
- package/esm/helpers/nats/getNatsConfig.js +61 -0
- package/esm/helpers/nats/getNatsConfig.js.map +1 -0
- package/esm/helpers/nats/isSessionExpiredError.d.ts +3 -0
- package/esm/helpers/nats/isSessionExpiredError.js +32 -0
- package/esm/helpers/nats/isSessionExpiredError.js.map +1 -0
- package/esm/helpers/nats/useNats.d.ts +12 -0
- package/esm/helpers/nats/useNats.js +72 -0
- package/esm/helpers/nats/useNats.js.map +1 -0
- package/esm/helpers/nats/useNatsSession.d.ts +28 -0
- package/esm/helpers/nats/useNatsSession.js +208 -0
- package/esm/helpers/nats/useNatsSession.js.map +1 -0
- package/esm/helpers/utils.d.ts +21 -0
- package/esm/helpers/utils.js +68 -3
- package/esm/helpers/utils.js.map +1 -1
- package/esm/locales/de.json +1 -0
- package/esm/locales/en.json +1 -0
- package/esm/locales/es.json +1 -0
- package/esm/locales/fr.json +1 -0
- package/esm/locales/it.json +1 -0
- package/esm/styles.css +1 -1
- package/esm/types/integration.d.ts +3 -0
- package/esm/types/integration.js +11 -0
- package/esm/types/integration.js.map +1 -0
- package/esm/version.d.ts +1 -1
- package/esm/version.js +1 -1
- package/package.json +5 -3
- package/src/components/AgeVerificationModal/AgeVerificationModal.tsx +6 -1
- package/src/components/Avatar/Avatar.stories.tsx +45 -28
- package/src/components/Avatar/Avatar.test.tsx +54 -0
- package/src/components/Avatar/Avatar.tsx +8 -12
- package/src/components/Avatar/AvatarView/AvatarComponent/components/FullbodyAvatar/fullbodyAvatar.tsx +12 -5
- package/src/components/Avatar/AvatarView/AvatarComponent/components/controllers/MorphTargetController.ts +2 -1
- package/src/components/Avatar/AvatarView/AvatarComponent/constants.ts +16 -4
- package/src/components/Avatar/__snapshots__/Avatar.test.tsx.snap +164 -0
- package/src/components/ChatBubble/ChatBubble.tsx +37 -12
- package/src/components/CompletionProviderStatus/CompletionProviderStatus.tsx +8 -4
- package/src/components/DateSelector/DateSelector.stories.tsx +11 -2
- package/src/components/DateSelector/DateSelector.tsx +28 -9
- package/src/components/FilePreview/FilePreview.tsx +28 -7
- package/src/components/MediaWidget/MediaItemWidget.tsx +26 -13
- package/src/components/MediaWidget/MediaItemWidget.utils.test.ts +45 -2
- package/src/components/MediaWidget/MediaItemWidget.utils.ts +39 -6
- package/src/components/MediaWidget/MediaPreviewModal.tsx +9 -7
- package/src/components/MemoriWidget/MemoriWidget.tsx +785 -331
- package/src/components/StartPanel/StartPanel.stories.tsx +22 -0
- package/src/components/StartPanel/StartPanel.test.tsx +64 -1
- package/src/components/StartPanel/__snapshots__/StartPanel.test.tsx.snap +261 -0
- package/src/components/UploadButton/UploadButton.tsx +30 -11
- package/src/components/UploadButton/UploadDocuments/UploadDocuments.tsx +148 -61
- package/src/components/UploadButton/__snapshots__/UploadButton.test.tsx.snap +2 -2
- package/src/components/layouts/WebsiteAssistant/website-assistant.css +2 -1
- package/src/components/layouts/layouts.stories.tsx +131 -12
- package/src/helpers/constants.ts +32 -1
- package/src/helpers/credits.ts +16 -1
- package/src/helpers/message.test.ts +28 -1
- package/src/helpers/message.ts +4 -4
- package/src/helpers/nats/getNatsConfig.ts +145 -0
- package/src/helpers/nats/isSessionExpiredError.test.ts +38 -0
- package/src/helpers/nats/isSessionExpiredError.ts +51 -0
- package/src/helpers/nats/useNats.ts +124 -0
- package/src/helpers/nats/useNatsSession.ts +394 -0
- package/src/helpers/utils.test.ts +162 -0
- package/src/helpers/utils.ts +108 -3
- package/src/index.stories.tsx +32 -0
- package/src/locales/de.json +1 -0
- package/src/locales/en.json +1 -0
- package/src/locales/es.json +1 -0
- package/src/locales/fr.json +1 -0
- package/src/locales/it.json +1 -0
- package/src/types/integration.test.ts +32 -0
- package/src/types/integration.ts +31 -0
- package/src/version.ts +1 -1
|
@@ -5,6 +5,8 @@ import { FileText as DocumentIcon } from 'lucide-react';
|
|
|
5
5
|
import { Modal } from '@memori.ai/ui';
|
|
6
6
|
import { useTranslation } from 'react-i18next';
|
|
7
7
|
import memoriApiClient from '@memori.ai/memori-api-client';
|
|
8
|
+
import { officeNativeExtensions } from '../../../helpers/constants';
|
|
9
|
+
import { isOfficeNativeFilename } from '../../../helpers/utils';
|
|
8
10
|
// Types
|
|
9
11
|
type PreviewFile = {
|
|
10
12
|
name: string;
|
|
@@ -165,64 +167,101 @@ const UploadDocuments: React.FC<UploadDocumentsProps> = ({
|
|
|
165
167
|
}
|
|
166
168
|
};
|
|
167
169
|
|
|
168
|
-
const
|
|
169
|
-
|
|
170
|
-
if (!window.XLSX) {
|
|
171
|
-
await new Promise((resolve, reject) => {
|
|
172
|
-
const script = document.createElement('script');
|
|
173
|
-
script.src = XLSX_URL;
|
|
174
|
-
script.onload = resolve;
|
|
175
|
-
script.onerror = reject;
|
|
176
|
-
document.head.appendChild(script);
|
|
177
|
-
});
|
|
178
|
-
}
|
|
170
|
+
const loadXLSXLibrary = async (): Promise<void> => {
|
|
171
|
+
if (window.XLSX) return;
|
|
179
172
|
|
|
180
|
-
|
|
181
|
-
const
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
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,
|
|
187
206
|
});
|
|
188
207
|
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
header: 1,
|
|
194
|
-
raw: false,
|
|
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);
|
|
195
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
|
+
});
|
|
196
225
|
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
}, []);
|
|
204
|
-
|
|
205
|
-
const formattedText = data.map((row: any[]) => {
|
|
206
|
-
return row
|
|
207
|
-
.map((cell, i) => {
|
|
208
|
-
const cellStr = (cell || '').toString();
|
|
209
|
-
return cellStr.padEnd(colWidths[i] + 2);
|
|
210
|
-
})
|
|
211
|
-
.join('|')
|
|
212
|
-
.trim();
|
|
213
|
-
});
|
|
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
|
+
}
|
|
214
232
|
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
}
|
|
233
|
+
text += `Sheet: ${sheetName}\n${formattedText.join('\n')}\n\n`;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
return text;
|
|
237
|
+
};
|
|
221
238
|
|
|
222
|
-
|
|
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`;
|
|
223
250
|
}
|
|
251
|
+
}
|
|
224
252
|
|
|
225
|
-
|
|
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
|
+
};
|
|
226
265
|
} catch (error) {
|
|
227
266
|
console.error('XLSX extraction failed:', error);
|
|
228
267
|
throw new Error(
|
|
@@ -235,21 +274,31 @@ const UploadDocuments: React.FC<UploadDocumentsProps> = ({
|
|
|
235
274
|
|
|
236
275
|
const processDocumentFile = async (
|
|
237
276
|
file: File
|
|
238
|
-
): Promise<{
|
|
239
|
-
|
|
277
|
+
): Promise<{
|
|
278
|
+
text: string | null;
|
|
279
|
+
textForAsset?: string;
|
|
280
|
+
uploadAsOriginal?: boolean;
|
|
281
|
+
}> => {
|
|
282
|
+
if (isOfficeNativeFilename(file.name)) {
|
|
283
|
+
return { text: null, uploadAsOriginal: true };
|
|
284
|
+
}
|
|
240
285
|
|
|
286
|
+
const ext = file.name.split('.').pop()?.toLowerCase() || '';
|
|
241
287
|
try {
|
|
242
288
|
let text: string | null = null;
|
|
289
|
+
let textForAsset: string | undefined;
|
|
243
290
|
|
|
244
|
-
if (
|
|
291
|
+
if (ext === 'pdf') {
|
|
245
292
|
text = await extractTextFromPDF(file);
|
|
246
|
-
} else if (['txt', 'md', 'json', 'csv', 'html'].includes(
|
|
293
|
+
} else if (['txt', 'md', 'json', 'csv', 'html'].includes(ext)) {
|
|
247
294
|
text = await file.text();
|
|
248
|
-
} else if (
|
|
249
|
-
|
|
295
|
+
} else if (ext === 'xlsx') {
|
|
296
|
+
const extracted = await extractTextFromXLSX(file);
|
|
297
|
+
text = extracted.display;
|
|
298
|
+
textForAsset = extracted.asset;
|
|
250
299
|
}
|
|
251
300
|
|
|
252
|
-
return { text };
|
|
301
|
+
return { text, textForAsset };
|
|
253
302
|
} catch (error) {
|
|
254
303
|
console.error('Document processing failed:', error);
|
|
255
304
|
throw new Error(
|
|
@@ -295,7 +344,12 @@ const UploadDocuments: React.FC<UploadDocumentsProps> = ({
|
|
|
295
344
|
throw new Error(response.resultMessage || 'Upload failed');
|
|
296
345
|
}
|
|
297
346
|
|
|
298
|
-
|
|
347
|
+
const assetURL = response.asset?.assetURL;
|
|
348
|
+
if (!assetURL) {
|
|
349
|
+
throw new Error('Upload failed: missing asset URL');
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
return assetURL;
|
|
299
353
|
};
|
|
300
354
|
|
|
301
355
|
const handleDocumentUpload = async (
|
|
@@ -360,11 +414,42 @@ const UploadDocuments: React.FC<UploadDocumentsProps> = ({
|
|
|
360
414
|
const fileId = Math.random().toString(36).substr(2, 9);
|
|
361
415
|
|
|
362
416
|
try {
|
|
363
|
-
const { text } =
|
|
417
|
+
const { text, textForAsset, uploadAsOriginal } =
|
|
418
|
+
await processDocumentFile(file);
|
|
419
|
+
|
|
420
|
+
if (uploadAsOriginal) {
|
|
421
|
+
// Office native format: upload the original file as asset, no text extraction
|
|
422
|
+
let assetUrl: string | undefined;
|
|
423
|
+
try {
|
|
424
|
+
assetUrl = await uploadAssetFile(file);
|
|
425
|
+
} catch (uploadError) {
|
|
426
|
+
console.error('Office asset upload failed:', uploadError);
|
|
427
|
+
onDocumentError?.({
|
|
428
|
+
message: t('upload.officeAssetUploadFailed', {
|
|
429
|
+
fileName: file.name,
|
|
430
|
+
defaultValue: `"${file.name}" could not be uploaded and was not added.`,
|
|
431
|
+
}),
|
|
432
|
+
severity: 'error',
|
|
433
|
+
});
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
if (!assetUrl) {
|
|
437
|
+
activeCount--;
|
|
438
|
+
onLoadingChange?.(true, activeCount);
|
|
439
|
+
continue;
|
|
440
|
+
}
|
|
364
441
|
|
|
365
|
-
|
|
442
|
+
processedFiles.push({
|
|
443
|
+
name: file.name,
|
|
444
|
+
id: fileId,
|
|
445
|
+
content: '',
|
|
446
|
+
mimeType: file.type,
|
|
447
|
+
textAssetUrl: assetUrl,
|
|
448
|
+
});
|
|
449
|
+
} else if (text) {
|
|
366
450
|
const baseName = file.name.replace(/\.[^/.]+$/, '') || file.name;
|
|
367
|
-
const
|
|
451
|
+
const assetText = textForAsset ?? text;
|
|
452
|
+
const textFile = new File([assetText], `${baseName}.txt`, {
|
|
368
453
|
type: 'text/plain',
|
|
369
454
|
});
|
|
370
455
|
|
|
@@ -429,7 +514,9 @@ const UploadDocuments: React.FC<UploadDocumentsProps> = ({
|
|
|
429
514
|
<input
|
|
430
515
|
ref={documentInputRef}
|
|
431
516
|
type="file"
|
|
432
|
-
accept=
|
|
517
|
+
accept={`.pdf,.txt,.md,.json,.xlsx,.csv,.html,${officeNativeExtensions.join(
|
|
518
|
+
','
|
|
519
|
+
)}`}
|
|
433
520
|
multiple
|
|
434
521
|
className="memori--upload-file-input"
|
|
435
522
|
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"
|
|
9
|
+
accept=".jpg,.jpeg,.png,.pdf,.txt,.json,.xlsx,.csv,.md,.html,.doc,.docx,.dotx,.xltx,.potx"
|
|
10
10
|
class="memori--upload-file-input"
|
|
11
11
|
multiple=""
|
|
12
12
|
style="display: none;"
|
|
@@ -60,7 +60,7 @@ exports[`renders UploadButton unchanged 1`] = `
|
|
|
60
60
|
class="memori--document-upload-wrapper"
|
|
61
61
|
>
|
|
62
62
|
<input
|
|
63
|
-
accept=".pdf,.txt,.md,.json,.xlsx,.csv,.html"
|
|
63
|
+
accept=".pdf,.txt,.md,.json,.xlsx,.csv,.html,.doc,.docx,.dotx,.xltx,.potx"
|
|
64
64
|
class="memori--upload-file-input"
|
|
65
65
|
multiple=""
|
|
66
66
|
type="file"
|
|
@@ -418,7 +418,8 @@
|
|
|
418
418
|
transform: none !important;
|
|
419
419
|
}
|
|
420
420
|
|
|
421
|
-
.memori-layout-website_assistant.memori--avatar-readyplayerme-full .memori--avatar-wrapper > div
|
|
421
|
+
.memori-layout-website_assistant.memori--avatar-readyplayerme-full .memori--avatar-wrapper > div,
|
|
422
|
+
.memori-layout-website_assistant.memori--avatar-avatar-configurator .memori--avatar-wrapper > div {
|
|
422
423
|
transform: scale(1.7) translate(0px, 10vh);
|
|
423
424
|
}
|
|
424
425
|
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import { Meta, Story } from '@storybook/react';
|
|
3
|
+
import { Spin } from '@memori.ai/ui';
|
|
3
4
|
import { memori, tenant, integration } from '../../mocks/data';
|
|
4
5
|
import Memori, { Props } from '../../index';
|
|
6
|
+
import { LayoutProps } from '../MemoriWidget/MemoriWidget';
|
|
5
7
|
import I18nWrapper from '../../I18nWrapper';
|
|
6
8
|
import { VisemeProvider } from '../../context/visemeContext';
|
|
7
9
|
import { ArtifactProvider } from '../MemoriArtifactSystem/context/ArtifactContext';
|
|
@@ -30,25 +32,18 @@ const Template: Story<Props> = args => (
|
|
|
30
32
|
|
|
31
33
|
const DefaultLayout = Template.bind({});
|
|
32
34
|
DefaultLayout.args = {
|
|
33
|
-
memoriName: '
|
|
34
|
-
ownerUserName: '
|
|
35
|
-
memoriID: '
|
|
35
|
+
memoriName: 'prova-sharepoint',
|
|
36
|
+
ownerUserName: 'Andrea-Patini',
|
|
37
|
+
memoriID: '019f129c-a5a3-74d3-b0c8-b7794437bb23',
|
|
36
38
|
ownerUserID: '91dbc9ba-b684-4fbe-9828-b5980af6cda9',
|
|
37
39
|
tenantID: 'aisuru-staging.aclambda.online',
|
|
38
40
|
engineURL: 'https://engine-staging.memori.ai/memori/v2',
|
|
39
41
|
apiURL: 'https://backend-staging.memori.ai/api/v2',
|
|
42
|
+
baseURL: 'https://aisuru-staging.aclambda.online',
|
|
40
43
|
layout: 'FULLPAGE',
|
|
41
44
|
uiLang: 'IT',
|
|
42
45
|
spokenLang: 'IT',
|
|
43
|
-
integrationID: '
|
|
44
|
-
autoStart: false,
|
|
45
|
-
sessionID: '' as string | undefined,
|
|
46
|
-
showUpload: true,
|
|
47
|
-
showReasoning: false,
|
|
48
|
-
showLogin: true,
|
|
49
|
-
multilingual: true,
|
|
50
|
-
avatar3dHidden: true,
|
|
51
|
-
showMessageConsumption: true,
|
|
46
|
+
integrationID: '019f1896-730d-7e19-9a88-6413f485247b',
|
|
52
47
|
};
|
|
53
48
|
|
|
54
49
|
export const Default = Template.bind({});
|
|
@@ -82,6 +77,130 @@ ChatOnly.args = {
|
|
|
82
77
|
layout: 'CHAT',
|
|
83
78
|
};
|
|
84
79
|
|
|
80
|
+
const CustomLayout: React.FC<LayoutProps> = ({
|
|
81
|
+
Header,
|
|
82
|
+
headerProps,
|
|
83
|
+
Avatar,
|
|
84
|
+
avatarProps,
|
|
85
|
+
Chat,
|
|
86
|
+
chatProps,
|
|
87
|
+
StartPanel,
|
|
88
|
+
startPanelProps,
|
|
89
|
+
integrationStyle,
|
|
90
|
+
integrationBackground,
|
|
91
|
+
sessionId,
|
|
92
|
+
hasUserActivatedSpeak,
|
|
93
|
+
loading = false,
|
|
94
|
+
poweredBy,
|
|
95
|
+
}) => (
|
|
96
|
+
<>
|
|
97
|
+
{integrationStyle}
|
|
98
|
+
{integrationBackground}
|
|
99
|
+
|
|
100
|
+
<Spin spinning={loading} className="memori-mycustom-layout">
|
|
101
|
+
{poweredBy}
|
|
102
|
+
|
|
103
|
+
<div className="memori-mycustom-layout--controls">
|
|
104
|
+
{sessionId && hasUserActivatedSpeak && Chat && chatProps ? (
|
|
105
|
+
<Chat {...chatProps} />
|
|
106
|
+
) : startPanelProps ? (
|
|
107
|
+
<StartPanel {...startPanelProps} />
|
|
108
|
+
) : null}
|
|
109
|
+
</div>
|
|
110
|
+
</Spin>
|
|
111
|
+
</>
|
|
112
|
+
);
|
|
113
|
+
|
|
114
|
+
export const Custom = Template.bind({});
|
|
115
|
+
Custom.args = {
|
|
116
|
+
...DefaultLayout.args,
|
|
117
|
+
uiLang: 'it',
|
|
118
|
+
showShare: true,
|
|
119
|
+
showSettings: true,
|
|
120
|
+
memori: memori,
|
|
121
|
+
tenant,
|
|
122
|
+
layout: 'FULLPAGE',
|
|
123
|
+
customLayout: CustomLayout,
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
export const WebsiteAssistant2 = Template.bind({});
|
|
127
|
+
WebsiteAssistant2.args = {
|
|
128
|
+
memoriName: 'Marconi Express',
|
|
129
|
+
ownerUserName: 'exmita',
|
|
130
|
+
memoriID: '1755979f-28f1-45d1-8f41-bb34e1f7756b',
|
|
131
|
+
ownerUserID: '3194fcd3-040b-4d5e-a925-691911fb1177',
|
|
132
|
+
tenantID: 'exmachina.aclambda.online',
|
|
133
|
+
engineURL: 'https://engine.memori.ai',
|
|
134
|
+
apiURL: 'https://backend.memori.ai',
|
|
135
|
+
baseURL: 'https://exmachina.aclambda.online',
|
|
136
|
+
uiLang: 'IT',
|
|
137
|
+
avatar3dHidden: true,
|
|
138
|
+
spokenLang: 'IT',
|
|
139
|
+
layout: 'WEBSITE_ASSISTANT',
|
|
140
|
+
};
|
|
141
|
+
|
|
142
|
+
export const WebsiteAssistant3 = Template.bind({});
|
|
143
|
+
WebsiteAssistant3.args = {
|
|
144
|
+
memoriName: 'Layout Storybook',
|
|
145
|
+
ownerUserName: 'Andrea-Patini',
|
|
146
|
+
memoriID: 'ae20fc5a-cc15-4db9-b7dd-2cd4a621b85e',
|
|
147
|
+
ownerUserID: '91dbc9ba-b684-4fbe-9828-b5980af6cda9',
|
|
148
|
+
tenantID: 'aisuru-staging.aclambda.online',
|
|
149
|
+
engineURL: 'https://engine-staging.memori.ai/memori/v2',
|
|
150
|
+
apiURL: 'https://backend-staging.memori.ai/api/v2',
|
|
151
|
+
baseURL: 'http://localhost:3000',
|
|
152
|
+
layout: 'WEBSITE_ASSISTANT',
|
|
153
|
+
avatar3dHidden: true,
|
|
154
|
+
uiLang: 'IT',
|
|
155
|
+
spokenLang: 'IT',
|
|
156
|
+
showOnlyLastMessages: true,
|
|
157
|
+
integrationID: '716f4728-919c-4015-aae1-88998a081c6f',
|
|
158
|
+
};
|
|
159
|
+
|
|
160
|
+
export const WebsiteAssistant = Template.bind({});
|
|
161
|
+
WebsiteAssistant.args = {
|
|
162
|
+
uiLang: 'EN',
|
|
163
|
+
showShare: false,
|
|
164
|
+
showSettings: true,
|
|
165
|
+
memori: memori,
|
|
166
|
+
tenant,
|
|
167
|
+
layout: 'WEBSITE_ASSISTANT',
|
|
168
|
+
memoriName: 'Memori',
|
|
169
|
+
memoriID: '25ced51c-3520-41af-8bbe-222d861b8e32',
|
|
170
|
+
ownerUserName: 'memoridev',
|
|
171
|
+
tenantID: 'www.aisuru.com',
|
|
172
|
+
apiURL: 'https://backend.memori.ai',
|
|
173
|
+
baseURL: 'https://www.aisuru.com',
|
|
174
|
+
spokenLang: 'EN',
|
|
175
|
+
context: {
|
|
176
|
+
SOURCE: 'WEBSITE',
|
|
177
|
+
PATH: '/en/',
|
|
178
|
+
},
|
|
179
|
+
multilingual: true,
|
|
180
|
+
integrationID: 'cb3c4776-7f0b-4f97-a773-c32a5d7a3bf1',
|
|
181
|
+
integration: {
|
|
182
|
+
...integration,
|
|
183
|
+
customData: JSON.stringify({
|
|
184
|
+
textColor: '#2a2a2a',
|
|
185
|
+
buttonBgColor: '#653165',
|
|
186
|
+
buttonTextColor: '#ffffff',
|
|
187
|
+
blurBackground: true,
|
|
188
|
+
innerBgColor: 'light',
|
|
189
|
+
innerBgAlpha: 0.8,
|
|
190
|
+
multilanguage: true,
|
|
191
|
+
avatar: 'readyplayerme',
|
|
192
|
+
avatarURL:
|
|
193
|
+
'https://assets.memori.ai/api/v2/asset/b791f77c-1a94-4272-829e-eca82fcc62b7.glb',
|
|
194
|
+
}),
|
|
195
|
+
},
|
|
196
|
+
};
|
|
197
|
+
|
|
198
|
+
export const WebsiteAssistantWithout3DAvatar = Template.bind({});
|
|
199
|
+
WebsiteAssistantWithout3DAvatar.args = {
|
|
200
|
+
...WebsiteAssistant.args,
|
|
201
|
+
avatar3dHidden: true,
|
|
202
|
+
};
|
|
203
|
+
|
|
85
204
|
export const HiddenChat = Template.bind({});
|
|
86
205
|
HiddenChat.args = {
|
|
87
206
|
...DefaultLayout.args,
|
package/src/helpers/constants.ts
CHANGED
|
@@ -36,7 +36,7 @@ export const getGroupedChatLanguages = () => {
|
|
|
36
36
|
popularLanguageCodes.includes(lang.value)
|
|
37
37
|
);
|
|
38
38
|
const all = chatLanguages.filter(lang => !popularLanguageCodes.includes(lang.value));
|
|
39
|
-
return {
|
|
39
|
+
return {
|
|
40
40
|
popular,
|
|
41
41
|
all,
|
|
42
42
|
};
|
|
@@ -44,6 +44,15 @@ export const getGroupedChatLanguages = () => {
|
|
|
44
44
|
|
|
45
45
|
export const uiLanguages = ['en', 'it', 'fr', 'es', 'de'];
|
|
46
46
|
|
|
47
|
+
/** Extensions uploaded as original Office binaries (no text extraction) */
|
|
48
|
+
export const officeNativeExtensions = [
|
|
49
|
+
'.doc',
|
|
50
|
+
'.docx',
|
|
51
|
+
'.dotx',
|
|
52
|
+
'.xltx',
|
|
53
|
+
'.potx',
|
|
54
|
+
] as const;
|
|
55
|
+
|
|
47
56
|
export const allowedMediaTypes = [
|
|
48
57
|
'image/jpeg',
|
|
49
58
|
'image/png',
|
|
@@ -51,6 +60,7 @@ export const allowedMediaTypes = [
|
|
|
51
60
|
'image/gif',
|
|
52
61
|
'application/msword',
|
|
53
62
|
'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
|
|
63
|
+
'application/vnd.openxmlformats-officedocument.wordprocessingml.template',
|
|
54
64
|
'application/vnd.ms-excel',
|
|
55
65
|
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
|
|
56
66
|
'application/pdf',
|
|
@@ -63,6 +73,27 @@ export const allowedMediaTypes = [
|
|
|
63
73
|
'model/gltf-binary',
|
|
64
74
|
];
|
|
65
75
|
|
|
76
|
+
/** Short badge labels for Office document cards */
|
|
77
|
+
export const officeMimeShortLabels: Record<string, string> = {
|
|
78
|
+
'application/msword': 'Word',
|
|
79
|
+
'application/vnd.openxmlformats-officedocument.wordprocessingml.document': 'Word',
|
|
80
|
+
'application/vnd.openxmlformats-officedocument.wordprocessingml.template': 'Word',
|
|
81
|
+
'application/vnd.ms-excel': 'Excel',
|
|
82
|
+
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet': 'Excel',
|
|
83
|
+
'application/vnd.openxmlformats-officedocument.spreadsheetml.template': 'Excel',
|
|
84
|
+
'application/vnd.openxmlformats-officedocument.presentationml.template': 'PPT',
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
export const officeExtensionShortLabels: Record<string, string> = {
|
|
88
|
+
DOC: 'Word',
|
|
89
|
+
DOCX: 'Word',
|
|
90
|
+
DOTX: 'Word',
|
|
91
|
+
XLS: 'Excel',
|
|
92
|
+
XLSX: 'Excel',
|
|
93
|
+
XLTX: 'Excel',
|
|
94
|
+
POTX: 'PPT',
|
|
95
|
+
};
|
|
96
|
+
|
|
66
97
|
export const anonTag = '👤';
|
|
67
98
|
|
|
68
99
|
export const prismSyntaxLangs = [
|
package/src/helpers/credits.ts
CHANGED
|
@@ -1,23 +1,37 @@
|
|
|
1
1
|
// POST http://localhost:3000/api/verify-tokens operation=session_creation userID=585ec0ff-e805-495e-b8fc-5b0b8dd288ff tenant=aisuru-staging-tokenized.aclambda.online
|
|
2
|
+
export type CreditsOperation =
|
|
3
|
+
| 'twin_creation'
|
|
4
|
+
| 'session_creation'
|
|
5
|
+
| 'import_document'
|
|
6
|
+
// accepted by the API and normalized to session_creation
|
|
7
|
+
| 'dt_session_creation';
|
|
8
|
+
|
|
2
9
|
export const getCredits = async ({
|
|
3
10
|
operation = 'session_creation',
|
|
4
11
|
baseUrl,
|
|
5
12
|
userID,
|
|
6
13
|
userName,
|
|
7
14
|
tenant,
|
|
15
|
+
characters,
|
|
8
16
|
}: {
|
|
9
|
-
operation?:
|
|
17
|
+
operation?: CreditsOperation;
|
|
10
18
|
baseUrl: string;
|
|
11
19
|
userID?: string | null;
|
|
12
20
|
userName?: string | null;
|
|
13
21
|
tenant: string;
|
|
22
|
+
characters?: number;
|
|
14
23
|
}): Promise<{
|
|
15
24
|
enough: boolean;
|
|
16
25
|
required: number;
|
|
26
|
+
tokens?: number;
|
|
17
27
|
}> => {
|
|
18
28
|
if (!userID && !userName) {
|
|
19
29
|
throw new Error('Either userID or userName must be provided');
|
|
20
30
|
}
|
|
31
|
+
if (operation === 'import_document' && characters == null) {
|
|
32
|
+
throw new Error('characters must be provided for import_document');
|
|
33
|
+
}
|
|
34
|
+
|
|
21
35
|
const resp = await fetch(`${baseUrl}/api/verify-tokens`, {
|
|
22
36
|
method: 'POST',
|
|
23
37
|
headers: {
|
|
@@ -28,6 +42,7 @@ export const getCredits = async ({
|
|
|
28
42
|
userID,
|
|
29
43
|
userName,
|
|
30
44
|
tenant,
|
|
45
|
+
...(operation === 'import_document' ? { characters } : {}),
|
|
31
46
|
}),
|
|
32
47
|
});
|
|
33
48
|
|
|
@@ -1,4 +1,24 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
renderMsg,
|
|
3
|
+
sanitizeMsg,
|
|
4
|
+
stripAttachmentTags,
|
|
5
|
+
stripAllInternalTags,
|
|
6
|
+
} from './message';
|
|
7
|
+
|
|
8
|
+
describe('sanitizeMsg', () => {
|
|
9
|
+
it('strips onerror from partner XSS img payload', () => {
|
|
10
|
+
const payload =
|
|
11
|
+
'<img src="immagine-inesistente.jpg" onerror="alert(\'XSS Eseguito!\')">';
|
|
12
|
+
const result = sanitizeMsg(payload);
|
|
13
|
+
expect(result).toBe('<img src="immagine-inesistente.jpg">');
|
|
14
|
+
expect(result).not.toMatch(/onerror/i);
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
it('strips svg onload handlers', () => {
|
|
18
|
+
const result = sanitizeMsg('<svg onload="alert(1)"></svg>');
|
|
19
|
+
expect(result).not.toMatch(/onload/i);
|
|
20
|
+
});
|
|
21
|
+
});
|
|
2
22
|
|
|
3
23
|
describe('renderMsg', () => {
|
|
4
24
|
it('should render message with reasoning and output tag adjacent correctly', () => {
|
|
@@ -11,6 +31,13 @@ describe('renderMsg', () => {
|
|
|
11
31
|
'<details class="memori-think"><summary>Reasoning...</summary>L\'utente vuole che io elenchi i parametri dell\'ALBERO ESISTENTE.</details>\n\n<output class="rails-query" style="display:none">\nproject = Project.find("68d99b05783713e16737d32b")\n</output>\n\n<p>Recupero tutti i 7 parametri dell\'ALBERO ESISTENTE...</p>'
|
|
12
32
|
);
|
|
13
33
|
});
|
|
34
|
+
|
|
35
|
+
it('strips onerror from XSS img payload in rendered output', () => {
|
|
36
|
+
const payload =
|
|
37
|
+
'<img src="immagine-inesistente.jpg" onerror="alert(\'XSS Eseguito!\')">';
|
|
38
|
+
const result = renderMsg(payload, false, 'Reasoning...', false);
|
|
39
|
+
expect(result.text).not.toMatch(/onerror/i);
|
|
40
|
+
});
|
|
14
41
|
});
|
|
15
42
|
|
|
16
43
|
describe('stripAttachmentTags', () => {
|
package/src/helpers/message.ts
CHANGED
|
@@ -85,6 +85,9 @@ export const truncateMessage = (message: string) => {
|
|
|
85
85
|
return truncatedMessage;
|
|
86
86
|
};
|
|
87
87
|
|
|
88
|
+
export const sanitizeMsg = (msg: string) =>
|
|
89
|
+
DOMPurify.sanitize(msg, { ADD_ATTR: ['target'] });
|
|
90
|
+
|
|
88
91
|
export const renderMsg = (
|
|
89
92
|
text: string,
|
|
90
93
|
useMathFormatting = false,
|
|
@@ -196,9 +199,6 @@ export const renderMsg = (
|
|
|
196
199
|
return { text: finalText };
|
|
197
200
|
} catch (e) {
|
|
198
201
|
console.error('Error rendering message:', e);
|
|
199
|
-
return { text };
|
|
202
|
+
return { text: sanitizeMsg(text) };
|
|
200
203
|
}
|
|
201
204
|
};
|
|
202
|
-
|
|
203
|
-
export const sanitizeMsg = (msg: string) =>
|
|
204
|
-
DOMPurify.sanitize(msg, { ADD_ATTR: ['target'] });
|