UNPKG

@memori.ai/memori-react

Version:

[![npm version](https://img.shields.io/github/package-json/v/memori-ai/memori-react)](https://www.npmjs.com/package/@memori.ai/memori-react) ![Tests](https://github.com/memori-ai/memori-react/workflows/CI/badge.svg?branch=main) ![TypeScript Support](https

355 lines 17.6 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const tslib_1 = require("tslib"); const jsx_runtime_1 = require("react/jsx-runtime"); const react_1 = tslib_1.__importStar(require("react")); const classnames_1 = tslib_1.__importDefault(require("classnames")); const Spin_1 = tslib_1.__importDefault(require("../../ui/Spin")); const Document_1 = require("../../icons/Document"); const Modal_1 = tslib_1.__importDefault(require("../../ui/Modal")); const react_i18next_1 = require("react-i18next"); const constants_1 = require("../../../helpers/constants"); const utils_1 = require("../../../helpers/utils"); const PDF_JS_VERSION = '3.11.174'; const WORKER_URL = `https://cdnjs.cloudflare.com/ajax/libs/pdf.js/${PDF_JS_VERSION}/pdf.worker.min.js`; const PDF_JS_URL = `https://cdnjs.cloudflare.com/ajax/libs/pdf.js/${PDF_JS_VERSION}/pdf.min.js`; const XLSX_URL = 'https://cdn.sheetjs.com/xlsx-0.20.0/package/dist/xlsx.full.min.js'; const UploadDocuments = ({ setDocumentPreviewFiles, authToken = '', client, sessionID = '', memoriID = '', maxDocuments, documentPreviewFiles, onLoadingChange, onDocumentError, onValidateFile, onValidatePayloadSize, }) => { const { t } = (0, react_i18next_1.useTranslation)(); const { backend } = client || { backend: { uploadAsset: null, uploadAssetUnlogged: null }, }; const [isLoading, setIsLoading] = (0, react_1.useState)(false); const [selectedFile, setSelectedFile] = (0, react_1.useState)(null); const documentInputRef = (0, react_1.useRef)(null); const setLoadingState = (loading, fileCount) => { setIsLoading(loading); onLoadingChange === null || onLoadingChange === void 0 ? void 0 : onLoadingChange(loading, fileCount); }; const validateDocumentFile = (file) => { if (onValidateFile) { return onValidateFile(file); } return true; }; const validatePayloadSize = (newDocuments) => { if (onValidatePayloadSize) { const result = onValidatePayloadSize(newDocuments); if (typeof result === 'boolean') { return result ? { valid: true } : { valid: false, message: '' }; } return result; } return { valid: true }; }; const extractTextFromPDF = async (file) => { try { if (!window.pdfjsLib) { await new Promise((resolve, reject) => { const script = document.createElement('script'); script.src = PDF_JS_URL; script.onload = () => { window.pdfjsLib.GlobalWorkerOptions.workerSrc = WORKER_URL; resolve(true); }; script.onerror = reject; document.head.appendChild(script); }); } const arrayBuffer = await file.arrayBuffer(); const pdf = await window.pdfjsLib.getDocument({ data: arrayBuffer }) .promise; let text = ''; for (let i = 1; i <= pdf.numPages; i++) { const page = await pdf.getPage(i); const content = await page.getTextContent(); const pageText = content.items .filter((item) => item.str && typeof item.str === 'string') .map((item) => item.str) .join(' '); text += pageText + '\n'; } return text; } catch (error) { console.error('PDF extraction failed:', error); throw new Error(`PDF extraction failed: ${error instanceof Error ? error.message : 'Unknown error'}`); } }; const loadXLSXLibrary = async () => { if (window.XLSX) return; await new Promise((resolve, reject) => { const script = document.createElement('script'); script.src = XLSX_URL; script.onload = resolve; script.onerror = reject; document.head.appendChild(script); }); }; const readXLSXWorkbook = async (file) => { await loadXLSXLibrary(); const arrayBuffer = await file.arrayBuffer(); return window.XLSX.read(arrayBuffer, { type: 'array', cellFormula: true, cellNF: true, cellText: true, cellDates: true, }); }; const formatXLSXWorkbookForDisplay = (workbook) => { let text = ''; for (const sheetName of workbook.SheetNames) { const worksheet = workbook.Sheets[sheetName]; const data = window.XLSX.utils.sheet_to_json(worksheet, { header: 1, raw: false, }); const colWidths = data.reduce((widths, row) => { row.forEach((cell, i) => { const cellWidth = (cell || '').toString().length; widths[i] = Math.max(widths[i] || 0, cellWidth); }); return widths; }, []); const formattedText = data.map((row) => { return row .map((cell, i) => { const cellStr = (cell || '').toString(); return cellStr.padEnd(colWidths[i] + 2); }) .join('|') .trim(); }); if (formattedText.length > 0) { const separator = colWidths .map((w) => '-'.repeat(w + 2)) .join('+'); formattedText.splice(1, 0, separator); } text += `Sheet: ${sheetName}\n${formattedText.join('\n')}\n\n`; } return text; }; const formatXLSXWorkbookAsCsv = (workbook) => { let text = ''; for (const sheetName of workbook.SheetNames) { const worksheet = workbook.Sheets[sheetName]; const csv = window.XLSX.utils.sheet_to_csv(worksheet, { raw: false }); if (csv.trim()) { text += `Sheet: ${sheetName}\n${csv}\n\n`; } } return text; }; const extractTextFromXLSX = async (file) => { try { const workbook = await readXLSXWorkbook(file); return { display: formatXLSXWorkbookForDisplay(workbook), asset: formatXLSXWorkbookAsCsv(workbook), }; } catch (error) { console.error('XLSX extraction failed:', error); throw new Error(`XLSX extraction failed: ${error instanceof Error ? error.message : 'Unknown error'}`); } }; const processDocumentFile = async (file) => { var _a; if ((0, utils_1.isOfficeNativeFilename)(file.name)) { return { text: null, uploadAsOriginal: true }; } const ext = ((_a = file.name.split('.').pop()) === null || _a === void 0 ? void 0 : _a.toLowerCase()) || ''; try { let text = null; let textForAsset; if (ext === 'pdf') { text = await extractTextFromPDF(file); } else if (['txt', 'md', 'json', 'csv', 'html'].includes(ext)) { text = await file.text(); } else if (ext === 'xlsx') { const extracted = await extractTextFromXLSX(file); text = extracted.display; textForAsset = extracted.asset; } return { text, textForAsset }; } catch (error) { console.error('Document processing failed:', error); throw new Error(`Failed to process "${file.name}": ${error instanceof Error ? error.message : 'Unknown error'}`); } }; const fileToDataUrl = (file) => new Promise((resolve, reject) => { const reader = new FileReader(); reader.onload = e => { var _a; return resolve(((_a = e.target) === null || _a === void 0 ? void 0 : _a.result) || ''); }; reader.onerror = () => reject(new Error('File reading failed')); reader.readAsDataURL(file); }); const uploadAssetFile = async (file) => { var _a; if (!client) { throw new Error('API client not configured properly for media upload'); } const fileDataUrl = await fileToDataUrl(file); let response; if (authToken && (backend === null || backend === void 0 ? void 0 : backend.uploadAsset)) { response = await backend.uploadAsset(file.name, fileDataUrl, authToken); } else if (memoriID && sessionID && (backend === null || backend === void 0 ? void 0 : backend.uploadAssetUnlogged)) { response = await backend.uploadAssetUnlogged(file.name, fileDataUrl, memoriID, sessionID); } else { throw new Error('Missing required parameters for upload'); } if (!response) { throw new Error('Upload failed'); } if (response.resultCode !== 0) { throw new Error(response.resultMessage || 'Upload failed'); } const assetURL = (_a = response.asset) === null || _a === void 0 ? void 0 : _a.assetURL; if (!assetURL) { throw new Error('Upload failed: missing asset URL'); } return assetURL; }; const handleDocumentUpload = async (e) => { var _a; const files = Array.from(e.target.files || []); if (files.length === 0) return; const currentMediaCount = documentPreviewFiles.length; const remainingSlots = maxDocuments ? Math.max(0, maxDocuments - currentMediaCount) : files.length; const filesToProcess = files.slice(0, remainingSlots); if (files.length > filesToProcess.length) { const skipped = files.length - filesToProcess.length; onDocumentError === null || onDocumentError === void 0 ? void 0 : onDocumentError({ message: (_a = t('upload.documentsNotAddedMaxAllowed', { count: skipped, max: maxDocuments !== null && maxDocuments !== void 0 ? maxDocuments : 10, defaultValue: `${skipped} document(s) not added (maximum ${maxDocuments !== null && maxDocuments !== void 0 ? maxDocuments : 10} files allowed).`, })) !== null && _a !== void 0 ? _a : `${skipped} document(s) not added (maximum ${maxDocuments !== null && maxDocuments !== void 0 ? maxDocuments : 10} files allowed).`, severity: 'warning', }); } if (filesToProcess.length === 0) { if (documentInputRef.current) { documentInputRef.current.value = ''; } return; } setLoadingState(true, filesToProcess.length); try { const processedFiles = []; let activeCount = filesToProcess.length; for (const file of filesToProcess) { if (!validateDocumentFile(file)) { activeCount--; onLoadingChange === null || onLoadingChange === void 0 ? void 0 : onLoadingChange(true, activeCount); continue; } const fileId = Math.random().toString(36).substr(2, 9); try { const { text, textForAsset, uploadAsOriginal } = await processDocumentFile(file); if (uploadAsOriginal) { let assetUrl; try { assetUrl = await uploadAssetFile(file); } catch (uploadError) { console.error('Office asset upload failed:', uploadError); onDocumentError === null || onDocumentError === void 0 ? void 0 : onDocumentError({ message: t('upload.officeAssetUploadFailed', { fileName: file.name, defaultValue: `"${file.name}" could not be uploaded and was not added.`, }), severity: 'error', }); } if (!assetUrl) { activeCount--; onLoadingChange === null || onLoadingChange === void 0 ? void 0 : onLoadingChange(true, activeCount); continue; } processedFiles.push({ name: file.name, id: fileId, content: '', mimeType: file.type, textAssetUrl: assetUrl, }); } else if (text) { const baseName = file.name.replace(/\.[^/.]+$/, '') || file.name; const assetText = textForAsset !== null && textForAsset !== void 0 ? textForAsset : text; const textFile = new File([assetText], `${baseName}.txt`, { type: 'text/plain', }); let textAssetUrl; try { textAssetUrl = await uploadAssetFile(textFile); } catch (uploadError) { console.error('Text asset upload failed:', uploadError); onDocumentError === null || onDocumentError === void 0 ? void 0 : onDocumentError({ message: t('upload.partialAssetUploadWarning', { fileName: file.name, defaultValue: 'Some file links could not be uploaded, but the document was added anyway.', }), severity: 'warning', }); } processedFiles.push({ name: file.name, id: fileId, content: text, mimeType: file.type, textAssetUrl, }); } else { activeCount--; onLoadingChange === null || onLoadingChange === void 0 ? void 0 : onLoadingChange(true, activeCount); } } catch (error) { activeCount--; onLoadingChange === null || onLoadingChange === void 0 ? void 0 : onLoadingChange(true, activeCount); console.error('File processing error:', error); onDocumentError === null || onDocumentError === void 0 ? void 0 : onDocumentError({ message: `${error instanceof Error ? error.message : 'Unknown error'}`, severity: 'warning', }); } } if (processedFiles.length > 0) { setDocumentPreviewFiles(processedFiles.map(file => ({ ...file, type: 'document', }))); } } finally { setLoadingState(false); if (documentInputRef.current) { documentInputRef.current.value = ''; } } }; return ((0, jsx_runtime_1.jsxs)("div", { className: "memori--document-upload-wrapper", children: [(0, jsx_runtime_1.jsx)("input", { ref: documentInputRef, type: "file", accept: `.pdf,.txt,.md,.json,.xlsx,.csv,.html,${constants_1.officeNativeExtensions.join(',')}`, multiple: true, className: "memori--upload-file-input", onChange: handleDocumentUpload }), (0, jsx_runtime_1.jsx)("button", { className: (0, classnames_1.default)('memori-button', 'memori-button--circle', 'memori-button--icon-only', 'memori-share-button--button', 'memori--conversation-button', 'memori--document-upload-button', { 'memori--error': false }), onClick: () => { var _a; return (_a = documentInputRef.current) === null || _a === void 0 ? void 0 : _a.click(); }, disabled: isLoading || (maxDocuments && documentPreviewFiles.length >= maxDocuments) || false, title: "Upload documents", children: isLoading ? ((0, jsx_runtime_1.jsx)(Spin_1.default, { spinning: true, className: "memori--upload-icon" })) : ((0, jsx_runtime_1.jsx)(react_1.default.Fragment, { children: (0, jsx_runtime_1.jsx)(Document_1.DocumentIcon, { className: "memori--upload-icon" }) })) }), (0, jsx_runtime_1.jsx)(Modal_1.default, { width: "80%", widthMd: "80%", open: !!selectedFile, className: "memori--modal-preview-file", onClose: () => setSelectedFile(null), closable: true, title: selectedFile === null || selectedFile === void 0 ? void 0 : selectedFile.name, children: (0, jsx_runtime_1.jsx)("div", { className: "memori--preview-content", style: { maxHeight: '70vh', overflowY: 'auto', textAlign: 'center', whiteSpace: 'pre-wrap', }, children: selectedFile === null || selectedFile === void 0 ? void 0 : selectedFile.content }) })] })); }; exports.default = UploadDocuments; //# sourceMappingURL=UploadDocuments.js.map