@memori.ai/memori-react 7.16.2 → 7.17.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.
- package/CHANGELOG.md +33 -0
- package/dist/components/Avatar/AvatarView/AvatarComponent/lights/Lights.d.ts +27 -0
- package/dist/components/Avatar/AvatarView/AvatarComponent/lights/Lights.js +52 -0
- package/dist/components/Avatar/AvatarView/AvatarComponent/lights/Lights.js.map +1 -0
- package/dist/components/Avatar/AvatarView/AvatarComponent/positionControls/positionControls.css +19 -7
- package/dist/components/Avatar/AvatarView/AvatarComponent/positionControls/positionControls.js +7 -7
- package/dist/components/Avatar/AvatarView/AvatarComponent/positionControls/positionControls.js.map +1 -1
- package/dist/components/Avatar/AvatarView/index.js +2 -3
- package/dist/components/Avatar/AvatarView/index.js.map +1 -1
- package/dist/components/ChatTextArea/ChatTextArea.css +55 -60
- package/dist/components/MemoriWidget/MemoriWidget.js +215 -138
- package/dist/components/MemoriWidget/MemoriWidget.js.map +1 -1
- package/dist/components/SettingsDrawer/SettingsDrawer.css +5 -0
- package/dist/components/SettingsDrawer/SettingsDrawer.d.ts +2 -1
- package/dist/components/SettingsDrawer/SettingsDrawer.js +6 -3
- package/dist/components/SettingsDrawer/SettingsDrawer.js.map +1 -1
- package/dist/components/UploadButton/UploadButton.d.ts +5 -0
- package/dist/components/UploadButton/UploadButton.js +49 -48
- package/dist/components/UploadButton/UploadButton.js.map +1 -1
- package/dist/components/ui/Slider.css +59 -44
- package/dist/context/visemeContext.d.ts +1 -1
- package/dist/context/visemeContext.js +2 -2
- package/dist/context/visemeContext.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/esm/components/Avatar/AvatarView/AvatarComponent/lights/Lights.d.ts +27 -0
- package/esm/components/Avatar/AvatarView/AvatarComponent/lights/Lights.js +48 -0
- package/esm/components/Avatar/AvatarView/AvatarComponent/lights/Lights.js.map +1 -0
- package/esm/components/Avatar/AvatarView/AvatarComponent/positionControls/positionControls.css +19 -7
- package/esm/components/Avatar/AvatarView/AvatarComponent/positionControls/positionControls.js +7 -7
- package/esm/components/Avatar/AvatarView/AvatarComponent/positionControls/positionControls.js.map +1 -1
- package/esm/components/Avatar/AvatarView/index.js +3 -4
- package/esm/components/Avatar/AvatarView/index.js.map +1 -1
- package/esm/components/ChatTextArea/ChatTextArea.css +55 -60
- package/esm/components/MemoriWidget/MemoriWidget.js +216 -139
- package/esm/components/MemoriWidget/MemoriWidget.js.map +1 -1
- package/esm/components/SettingsDrawer/SettingsDrawer.css +5 -0
- package/esm/components/SettingsDrawer/SettingsDrawer.d.ts +2 -1
- package/esm/components/SettingsDrawer/SettingsDrawer.js +6 -3
- package/esm/components/SettingsDrawer/SettingsDrawer.js.map +1 -1
- package/esm/components/UploadButton/UploadButton.d.ts +5 -0
- package/esm/components/UploadButton/UploadButton.js +50 -49
- package/esm/components/UploadButton/UploadButton.js.map +1 -1
- package/esm/components/ui/Slider.css +59 -44
- package/esm/context/visemeContext.d.ts +1 -1
- package/esm/context/visemeContext.js +2 -2
- package/esm/context/visemeContext.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/package.json +1 -2
- package/src/components/Avatar/AvatarView/AvatarComponent/lights/Lights.tsx +145 -0
- package/src/components/Avatar/AvatarView/AvatarComponent/positionControls/positionControls.css +19 -7
- package/src/components/Avatar/AvatarView/AvatarComponent/positionControls/positionControls.tsx +6 -14
- package/src/components/Avatar/AvatarView/index.tsx +5 -14
- package/src/components/ChatTextArea/ChatTextArea.css +55 -60
- package/src/components/MemoriWidget/MemoriWidget.tsx +337 -187
- package/src/components/SettingsDrawer/SettingsDrawer.css +5 -0
- package/src/components/SettingsDrawer/SettingsDrawer.tsx +29 -11
- package/src/components/UploadButton/UploadButton.tsx +139 -118
- package/src/components/UploadButton/__snapshots__/UploadButton.test.tsx.snap +3 -52
- package/src/components/ui/Slider.css +59 -44
- package/src/context/visemeContext.tsx +2 -2
- 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
|
@@ -8,6 +8,7 @@ import Button from '../ui/Button';
|
|
|
8
8
|
import { Props as WidgetProps } from '../MemoriWidget/MemoriWidget';
|
|
9
9
|
import { useState } from 'react';
|
|
10
10
|
import Slider from '../ui/Slider';
|
|
11
|
+
import Tooltip from '../ui/Tooltip';
|
|
11
12
|
export interface Props {
|
|
12
13
|
open: boolean;
|
|
13
14
|
layout?: WidgetProps['layout'];
|
|
@@ -26,6 +27,7 @@ export interface Props {
|
|
|
26
27
|
enablePositionControls?: boolean;
|
|
27
28
|
setEnablePositionControls: (value: boolean) => void;
|
|
28
29
|
isAvatar3d?: boolean;
|
|
30
|
+
speakerMuted?: boolean;
|
|
29
31
|
}
|
|
30
32
|
|
|
31
33
|
const silenceSeconds = [2, 3, 5, 10, 15, 20, 30, 60];
|
|
@@ -34,6 +36,7 @@ const SettingsDrawer = ({
|
|
|
34
36
|
open,
|
|
35
37
|
layout = 'DEFAULT',
|
|
36
38
|
onClose,
|
|
39
|
+
speakerMuted,
|
|
37
40
|
microphoneMode = 'HOLD_TO_TALK',
|
|
38
41
|
continuousSpeechTimeout,
|
|
39
42
|
setMicrophoneMode,
|
|
@@ -87,17 +90,33 @@ const SettingsDrawer = ({
|
|
|
87
90
|
</Button>
|
|
88
91
|
)}
|
|
89
92
|
</RadioGroup.Option>
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
+
|
|
94
|
+
<Tooltip
|
|
95
|
+
content={
|
|
96
|
+
speakerMuted ? t('write_and_speak.continuousSpeechDisabled') : ''
|
|
97
|
+
}
|
|
98
|
+
disabled={!speakerMuted}
|
|
93
99
|
>
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
100
|
+
<RadioGroup.Option
|
|
101
|
+
value="CONTINUOUS"
|
|
102
|
+
className={`memori-settings-drawer--microphoneMode-radio-button ${
|
|
103
|
+
speakerMuted
|
|
104
|
+
? 'memori-settings-drawer--microphoneMode-radio-button-disabled'
|
|
105
|
+
: ''
|
|
106
|
+
}`}
|
|
107
|
+
>
|
|
108
|
+
{({ checked }) => (
|
|
109
|
+
<Button
|
|
110
|
+
primary={checked}
|
|
111
|
+
outlined={!checked}
|
|
112
|
+
disabled={speakerMuted}
|
|
113
|
+
>
|
|
114
|
+
{t('write_and_speak.continuousSpeechLabel') ||
|
|
115
|
+
'Continuous speech'}
|
|
116
|
+
</Button>
|
|
117
|
+
)}
|
|
118
|
+
</RadioGroup.Option>
|
|
119
|
+
</Tooltip>
|
|
101
120
|
</RadioGroup>
|
|
102
121
|
</div>
|
|
103
122
|
|
|
@@ -159,7 +178,6 @@ const SettingsDrawer = ({
|
|
|
159
178
|
{isAvatar3d && (
|
|
160
179
|
<>
|
|
161
180
|
<div className="memori-settings-drawer--field controls">
|
|
162
|
-
|
|
163
181
|
<label
|
|
164
182
|
htmlFor="avatarType"
|
|
165
183
|
className="memori-settings-drawer-label"
|
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
import React, { useState, useRef
|
|
1
|
+
import React, { useState, useRef } from 'react';
|
|
2
2
|
import cx from 'classnames';
|
|
3
|
-
import ConvertApi from 'convertapi-js';
|
|
4
3
|
import UploadIcon from '../icons/Upload';
|
|
5
4
|
import Spin from '../ui/Spin';
|
|
6
5
|
import Alert from '../ui/Alert';
|
|
@@ -14,13 +13,22 @@ type UploadError = {
|
|
|
14
13
|
|
|
15
14
|
/**
|
|
16
15
|
* FileUploadButton component allows users to upload and convert files to text
|
|
17
|
-
* Supports PDF
|
|
18
|
-
*
|
|
16
|
+
* Supports PDF and TXT files up to 10MB
|
|
17
|
+
* Extracts text from PDFs using PDF.js
|
|
19
18
|
*/
|
|
20
19
|
|
|
21
20
|
const MAX_FILE_SIZE = 10 * 1024 * 1024; // 10MB
|
|
22
21
|
const MAX_TEXT_LENGTH = 100000; // 100,000 characters
|
|
23
|
-
const
|
|
22
|
+
const PDF_JS_VERSION = '3.11.174'; // Last stable version with .min.js files
|
|
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
|
+
|
|
26
|
+
// Add type definition for pdfjsLib
|
|
27
|
+
declare global {
|
|
28
|
+
interface Window {
|
|
29
|
+
pdfjsLib: any;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
24
32
|
|
|
25
33
|
const FileUploadButton = ({
|
|
26
34
|
setPreviewFiles,
|
|
@@ -31,21 +39,19 @@ const FileUploadButton = ({
|
|
|
31
39
|
}) => {
|
|
32
40
|
// State for loading indicator
|
|
33
41
|
const [isLoading, setIsLoading] = useState(false);
|
|
34
|
-
// State for tracking upload errors
|
|
42
|
+
// State for tracking upload errors
|
|
35
43
|
const [errors, setErrors] = useState<UploadError[]>([]);
|
|
36
44
|
// Reference to hidden file input
|
|
37
45
|
const fileInputRef = useRef<HTMLInputElement>(null);
|
|
38
|
-
// State for ConvertAPI authentication token
|
|
39
|
-
const [convertapiToken, setConvertapiToken] = useState<string>();
|
|
40
46
|
|
|
41
47
|
// Clear all errors
|
|
42
48
|
const clearErrors = () => setErrors([]);
|
|
43
|
-
|
|
49
|
+
|
|
44
50
|
// Remove a specific error by message
|
|
45
51
|
const removeError = (errorMessage: string) => {
|
|
46
52
|
setErrors(prev => prev.filter(e => e.message !== errorMessage));
|
|
47
53
|
};
|
|
48
|
-
|
|
54
|
+
|
|
49
55
|
// Add a new error and auto-remove after 5 seconds
|
|
50
56
|
const addError = (error: UploadError) => {
|
|
51
57
|
setErrors(prev => [...prev, error]);
|
|
@@ -56,30 +62,57 @@ const FileUploadButton = ({
|
|
|
56
62
|
};
|
|
57
63
|
|
|
58
64
|
/**
|
|
59
|
-
*
|
|
60
|
-
*
|
|
65
|
+
* Extracts text from PDF using PDF.js
|
|
66
|
+
* @param file PDF file to process
|
|
67
|
+
* @returns Promise resolving to extracted text
|
|
61
68
|
*/
|
|
62
|
-
const
|
|
69
|
+
const extractTextFromPDF = async (file: File): Promise<string> => {
|
|
63
70
|
try {
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
71
|
+
// Load PDF.js if not already loaded
|
|
72
|
+
if (!window.pdfjsLib) {
|
|
73
|
+
await new Promise((resolve, reject) => {
|
|
74
|
+
const script = document.createElement('script');
|
|
75
|
+
script.src = PDF_JS_URL;
|
|
76
|
+
script.onload = () => {
|
|
77
|
+
// Set up worker
|
|
78
|
+
window.pdfjsLib.GlobalWorkerOptions.workerSrc = WORKER_URL;
|
|
79
|
+
resolve(true);
|
|
80
|
+
};
|
|
81
|
+
script.onerror = reject;
|
|
82
|
+
document.head.appendChild(script);
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// Extract text from PDF
|
|
87
|
+
const arrayBuffer = await file.arrayBuffer();
|
|
88
|
+
// Get PDF document
|
|
89
|
+
const pdf = await window.pdfjsLib.getDocument({ data: arrayBuffer })
|
|
90
|
+
.promise;
|
|
91
|
+
let text = '';
|
|
92
|
+
|
|
93
|
+
// Iterate through each page and extract text
|
|
94
|
+
for (let i = 1; i <= pdf.numPages; i++) {
|
|
95
|
+
const page = await pdf.getPage(i);
|
|
96
|
+
const content = await page.getTextContent();
|
|
97
|
+
// Filter out non-string items and join text
|
|
98
|
+
const pageText = content.items
|
|
99
|
+
.filter((item: any) => item.str && typeof item.str === 'string')
|
|
100
|
+
.map((item: any) => item.str)
|
|
101
|
+
.join(' ');
|
|
102
|
+
text += pageText + '\n';
|
|
68
103
|
}
|
|
69
|
-
|
|
104
|
+
|
|
105
|
+
// Return extracted text
|
|
106
|
+
return text;
|
|
70
107
|
} catch (error) {
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
108
|
+
throw new Error(
|
|
109
|
+
`PDF extraction failed: ${
|
|
110
|
+
error instanceof Error ? error.message : 'Unknown error'
|
|
111
|
+
}`
|
|
112
|
+
);
|
|
75
113
|
}
|
|
76
114
|
};
|
|
77
115
|
|
|
78
|
-
// Fetch token on component mount
|
|
79
|
-
useEffect(() => {
|
|
80
|
-
fetchConvertapiToken();
|
|
81
|
-
}, []);
|
|
82
|
-
|
|
83
116
|
/**
|
|
84
117
|
* Validates uploaded file
|
|
85
118
|
* Checks file type and size restrictions
|
|
@@ -88,21 +121,26 @@ const FileUploadButton = ({
|
|
|
88
121
|
*/
|
|
89
122
|
const validateFile = (file: File): boolean => {
|
|
90
123
|
const fileExt = `.${file.name.split('.').pop()?.toLowerCase()}`;
|
|
124
|
+
const ALLOWED_FILE_TYPES = ['.pdf', '.txt'];
|
|
91
125
|
|
|
92
126
|
if (!ALLOWED_FILE_TYPES.includes(fileExt)) {
|
|
93
127
|
addError({
|
|
94
|
-
message: `File type "${fileExt}" is not supported. Please use: ${ALLOWED_FILE_TYPES.join(
|
|
128
|
+
message: `File type "${fileExt}" is not supported. Please use: ${ALLOWED_FILE_TYPES.join(
|
|
129
|
+
', '
|
|
130
|
+
)}`,
|
|
95
131
|
severity: 'error',
|
|
96
|
-
fileId: file.name
|
|
132
|
+
fileId: file.name,
|
|
97
133
|
});
|
|
98
134
|
return false;
|
|
99
135
|
}
|
|
100
136
|
|
|
101
137
|
if (file.size > MAX_FILE_SIZE) {
|
|
102
138
|
addError({
|
|
103
|
-
message: `File "${file.name}" exceeds ${
|
|
139
|
+
message: `File "${file.name}" exceeds ${
|
|
140
|
+
MAX_FILE_SIZE / 1024 / 1024
|
|
141
|
+
}MB limit`,
|
|
104
142
|
severity: 'error',
|
|
105
|
-
fileId: file.name
|
|
143
|
+
fileId: file.name,
|
|
106
144
|
});
|
|
107
145
|
return false;
|
|
108
146
|
}
|
|
@@ -111,46 +149,28 @@ const FileUploadButton = ({
|
|
|
111
149
|
};
|
|
112
150
|
|
|
113
151
|
/**
|
|
114
|
-
*
|
|
115
|
-
* @param file File to
|
|
116
|
-
* @returns Promise resolving to
|
|
152
|
+
* Processes file to extract text content
|
|
153
|
+
* @param file File to process
|
|
154
|
+
* @returns Promise resolving to extracted text or null if processing fails
|
|
117
155
|
*/
|
|
118
|
-
const
|
|
119
|
-
|
|
120
|
-
addError({
|
|
121
|
-
message: 'File conversion service not initialized',
|
|
122
|
-
severity: 'error'
|
|
123
|
-
});
|
|
124
|
-
return null;
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
const fileExt = file.name.split('.').pop()?.toLowerCase() || 'pdf';
|
|
156
|
+
const processFile = async (file: File): Promise<string | null> => {
|
|
157
|
+
const fileExt = file.name.split('.').pop()?.toLowerCase() || '';
|
|
128
158
|
|
|
129
159
|
try {
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
// Convert file to text
|
|
138
|
-
const result = await convertApi.convert(fileExt, 'txt', params);
|
|
139
|
-
const fileUrl = result.files[0].Url;
|
|
140
|
-
|
|
141
|
-
// Fetch converted text content
|
|
142
|
-
const response = await fetch(fileUrl);
|
|
143
|
-
if (!response.ok) {
|
|
144
|
-
throw new Error(`HTTP error! status: ${response.status}`);
|
|
160
|
+
let text: string | null = null;
|
|
161
|
+
|
|
162
|
+
if (fileExt === 'pdf') {
|
|
163
|
+
text = await extractTextFromPDF(file);
|
|
164
|
+
} else if (fileExt === 'txt') {
|
|
165
|
+
text = await file.text();
|
|
145
166
|
}
|
|
146
|
-
const text = await response.text();
|
|
147
167
|
|
|
148
168
|
// Check text length limit
|
|
149
|
-
if (text.length > MAX_TEXT_LENGTH) {
|
|
169
|
+
if (text && text.length > MAX_TEXT_LENGTH) {
|
|
150
170
|
addError({
|
|
151
171
|
message: `File "${file.name}" content exceeds ${MAX_TEXT_LENGTH} characters`,
|
|
152
172
|
severity: 'error',
|
|
153
|
-
fileId: file.name
|
|
173
|
+
fileId: file.name,
|
|
154
174
|
});
|
|
155
175
|
return null;
|
|
156
176
|
}
|
|
@@ -158,9 +178,11 @@ const FileUploadButton = ({
|
|
|
158
178
|
return text;
|
|
159
179
|
} catch (error) {
|
|
160
180
|
addError({
|
|
161
|
-
message: `Failed to
|
|
181
|
+
message: `Failed to process "${file.name}": ${
|
|
182
|
+
error instanceof Error ? error.message : 'Unknown error'
|
|
183
|
+
}`,
|
|
162
184
|
severity: 'error',
|
|
163
|
-
fileId: file.name
|
|
185
|
+
fileId: file.name,
|
|
164
186
|
});
|
|
165
187
|
return null;
|
|
166
188
|
}
|
|
@@ -168,8 +190,8 @@ const FileUploadButton = ({
|
|
|
168
190
|
|
|
169
191
|
/**
|
|
170
192
|
* Handles file selection event
|
|
171
|
-
* Validates files and
|
|
172
|
-
* Updates preview files state with
|
|
193
|
+
* Validates files and processes them to extract text
|
|
194
|
+
* Updates preview files state with processed content
|
|
173
195
|
*/
|
|
174
196
|
const handleFileSelect = async (e: React.ChangeEvent<HTMLInputElement>) => {
|
|
175
197
|
const files = Array.from(e.target.files || []);
|
|
@@ -177,7 +199,7 @@ const FileUploadButton = ({
|
|
|
177
199
|
|
|
178
200
|
setIsLoading(true);
|
|
179
201
|
clearErrors();
|
|
180
|
-
|
|
202
|
+
|
|
181
203
|
const newPreviewFiles: { name: string; id: string; content: string }[] = [];
|
|
182
204
|
|
|
183
205
|
// Process each selected file
|
|
@@ -185,24 +207,24 @@ const FileUploadButton = ({
|
|
|
185
207
|
if (!validateFile(file)) continue;
|
|
186
208
|
|
|
187
209
|
const fileId = Math.random().toString(36).substr(2, 9);
|
|
188
|
-
const text = await
|
|
210
|
+
const text = await processFile(file);
|
|
189
211
|
|
|
190
212
|
if (text) {
|
|
191
213
|
newPreviewFiles.push({
|
|
192
214
|
name: file.name,
|
|
193
215
|
id: fileId,
|
|
194
|
-
content: text
|
|
216
|
+
content: text,
|
|
195
217
|
});
|
|
196
218
|
}
|
|
197
219
|
}
|
|
198
220
|
|
|
199
|
-
// Update preview files if any
|
|
221
|
+
// Update preview files if any processing succeeded
|
|
200
222
|
if (newPreviewFiles.length > 0) {
|
|
201
223
|
setPreviewFiles(newPreviewFiles);
|
|
202
224
|
if (newPreviewFiles.length < files.length) {
|
|
203
225
|
addError({
|
|
204
226
|
message: 'Some files were not processed successfully',
|
|
205
|
-
severity: 'warning'
|
|
227
|
+
severity: 'warning',
|
|
206
228
|
});
|
|
207
229
|
}
|
|
208
230
|
}
|
|
@@ -213,56 +235,55 @@ const FileUploadButton = ({
|
|
|
213
235
|
}
|
|
214
236
|
};
|
|
215
237
|
|
|
216
|
-
console.log(errors);
|
|
217
|
-
|
|
218
238
|
return (
|
|
219
239
|
<div className="relative file-upload-wrapper">
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
240
|
+
{/* Hidden file input triggered by button click */}
|
|
241
|
+
<input
|
|
242
|
+
ref={fileInputRef}
|
|
243
|
+
type="file"
|
|
244
|
+
accept=".pdf,.txt"
|
|
245
|
+
className="memori--upload-file-input"
|
|
246
|
+
onChange={handleFileSelect}
|
|
247
|
+
multiple
|
|
248
|
+
/>
|
|
249
|
+
|
|
250
|
+
{/* Upload button with loading state */}
|
|
251
|
+
<button
|
|
252
|
+
className={cx(
|
|
253
|
+
'memori-button',
|
|
254
|
+
'memori-button--circle',
|
|
255
|
+
'memori-button--icon-only',
|
|
256
|
+
'memori-share-button--button',
|
|
257
|
+
'memori--conversation-button',
|
|
258
|
+
{ 'memori--error': errors.length > 0 }
|
|
259
|
+
)}
|
|
260
|
+
onClick={() => fileInputRef.current?.click()}
|
|
261
|
+
disabled={isLoading}
|
|
262
|
+
title="Upload file"
|
|
263
|
+
>
|
|
264
|
+
{isLoading ? (
|
|
265
|
+
<Spin spinning className="memori--upload-icon" />
|
|
266
|
+
) : (
|
|
267
|
+
<UploadIcon className="memori--upload-icon" />
|
|
268
|
+
)}
|
|
269
|
+
</button>
|
|
270
|
+
|
|
271
|
+
{/* Error messages container */}
|
|
272
|
+
<div className="memori--error-message-container">
|
|
273
|
+
{errors.map((error, index) => (
|
|
274
|
+
<Alert
|
|
275
|
+
key={`${error.message}-${index}`}
|
|
276
|
+
open={true}
|
|
277
|
+
type={error.severity}
|
|
278
|
+
title={'File upload failed'}
|
|
279
|
+
description={error.message}
|
|
280
|
+
onClose={() => removeError(error.message)}
|
|
281
|
+
width="350px"
|
|
282
|
+
/>
|
|
283
|
+
))}
|
|
284
|
+
</div>
|
|
263
285
|
</div>
|
|
264
|
-
</div>
|
|
265
286
|
);
|
|
266
287
|
};
|
|
267
288
|
|
|
268
|
-
export default FileUploadButton;
|
|
289
|
+
export default FileUploadButton;
|
|
@@ -6,13 +6,13 @@ exports[`renders UploadButton unchanged 1`] = `
|
|
|
6
6
|
class="relative file-upload-wrapper"
|
|
7
7
|
>
|
|
8
8
|
<input
|
|
9
|
-
accept=".pdf,.
|
|
9
|
+
accept=".pdf,.txt"
|
|
10
10
|
class="memori--upload-file-input"
|
|
11
11
|
multiple=""
|
|
12
12
|
type="file"
|
|
13
13
|
/>
|
|
14
14
|
<button
|
|
15
|
-
class="memori-button memori-button--circle memori-button--icon-only memori-share-button--button memori--conversation-button
|
|
15
|
+
class="memori-button memori-button--circle memori-button--icon-only memori-share-button--button memori--conversation-button"
|
|
16
16
|
title="Upload file"
|
|
17
17
|
>
|
|
18
18
|
<svg
|
|
@@ -30,56 +30,7 @@ exports[`renders UploadButton unchanged 1`] = `
|
|
|
30
30
|
</button>
|
|
31
31
|
<div
|
|
32
32
|
class="memori--error-message-container"
|
|
33
|
-
|
|
34
|
-
<div
|
|
35
|
-
class="memori-alert memori-alert--error"
|
|
36
|
-
>
|
|
37
|
-
<style>
|
|
38
|
-
|
|
39
|
-
.memori-alert {
|
|
40
|
-
--memori-alert--width: 300px;
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
</style>
|
|
44
|
-
<div
|
|
45
|
-
class="memori-alert--container ease-out duration-300 opacity-0 translate-y-4"
|
|
46
|
-
>
|
|
47
|
-
<div
|
|
48
|
-
class="memori-alert--content"
|
|
49
|
-
>
|
|
50
|
-
<div
|
|
51
|
-
class="memori-alert--title"
|
|
52
|
-
>
|
|
53
|
-
Failed to initialize file conversion service. Please try again later.
|
|
54
|
-
</div>
|
|
55
|
-
</div>
|
|
56
|
-
<div
|
|
57
|
-
class="memori-alert--actions"
|
|
58
|
-
>
|
|
59
|
-
<button
|
|
60
|
-
class="memori-button memori-button--ghost memori-button--circle memori-button--padded memori-button--icon-only memori-alert--close"
|
|
61
|
-
title="close"
|
|
62
|
-
>
|
|
63
|
-
<span
|
|
64
|
-
class="memori-button--icon"
|
|
65
|
-
>
|
|
66
|
-
<svg
|
|
67
|
-
aria-hidden="true"
|
|
68
|
-
focusable="false"
|
|
69
|
-
role="img"
|
|
70
|
-
viewBox="0 0 1024 1024"
|
|
71
|
-
xmlns="http://www.w3.org/2000/svg"
|
|
72
|
-
>
|
|
73
|
-
<path
|
|
74
|
-
d="M563.8 512l262.5-312.9c4.4-5.2.7-13.1-6.1-13.1h-79.8c-4.7 0-9.2 2.1-12.3 5.7L511.6 449.8 295.1 191.7c-3-3.6-7.5-5.7-12.3-5.7H203c-6.8 0-10.5 7.9-6.1 13.1L459.4 512 196.9 824.9A7.95 7.95 0 0 0 203 838h79.8c4.7 0 9.2-2.1 12.3-5.7l216.5-258.1 216.5 258.1c3 3.6 7.5 5.7 12.3 5.7h79.8c6.8 0 10.5-7.9 6.1-13.1L563.8 512z"
|
|
75
|
-
/>
|
|
76
|
-
</svg>
|
|
77
|
-
</span>
|
|
78
|
-
</button>
|
|
79
|
-
</div>
|
|
80
|
-
</div>
|
|
81
|
-
</div>
|
|
82
|
-
</div>
|
|
33
|
+
/>
|
|
83
34
|
</div>
|
|
84
35
|
</div>
|
|
85
36
|
`;
|