@clikvn/agent-widget-embedded 0.0.10-dev → 0.0.12-dev
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/dist/commons/constants/variables.d.ts +4 -0
- package/dist/commons/constants/variables.d.ts.map +1 -1
- package/dist/components/Chat/Icons.d.ts +19 -1
- package/dist/components/Chat/Icons.d.ts.map +1 -1
- package/dist/components/Chat/Message.d.ts.map +1 -1
- package/dist/components/Chat/MultimodalInput.d.ts +1 -1
- package/dist/components/Chat/MultimodalInput.d.ts.map +1 -1
- package/dist/components/Chat/SuggestedActions.d.ts +12 -0
- package/dist/components/Chat/SuggestedActions.d.ts.map +1 -0
- package/dist/features/AgentWidget/index.d.ts +8 -2
- package/dist/features/AgentWidget/index.d.ts.map +1 -1
- package/dist/hooks/useConfiguration.d.ts +8 -1
- package/dist/hooks/useConfiguration.d.ts.map +1 -1
- package/dist/index.html +45 -44
- package/dist/types/common.type.d.ts +6 -0
- package/dist/types/common.type.d.ts.map +1 -1
- package/dist/web.js +1 -1
- package/package.json +3 -2
- package/.eslintrc +0 -34
- package/.prettierrc +0 -8
- package/dist/components/Chat/AudioRecording.d.ts +0 -9
- package/dist/components/Chat/AudioRecording.d.ts.map +0 -1
- package/dist/hooks/useConnection.d.ts +0 -15
- package/dist/hooks/useConnection.d.ts.map +0 -1
- package/dist/services/user.service.d.ts +0 -3
- package/dist/services/user.service.d.ts.map +0 -1
- package/dist/types/agentType.d.ts +0 -11
- package/dist/types/agentType.d.ts.map +0 -1
- package/src/assets/common.css +0 -148
- package/src/assets/tailwindcss.css +0 -3
- package/src/commons/constants/index.ts +0 -1
- package/src/commons/constants/variables.ts +0 -20
- package/src/components/Agent/index.tsx +0 -14
- package/src/components/Chat/AudioPlayer.tsx +0 -44
- package/src/components/Chat/Chat.tsx +0 -91
- package/src/components/Chat/Icons.tsx +0 -930
- package/src/components/Chat/Markdown.tsx +0 -335
- package/src/components/Chat/Message.tsx +0 -191
- package/src/components/Chat/MultimodalInput.tsx +0 -479
- package/src/components/Chat/Overview.tsx +0 -46
- package/src/components/Chat/PreviewAttachment.tsx +0 -46
- package/src/components/Chat/ui/Button.tsx +0 -55
- package/src/components/Chat/ui/Textarea.tsx +0 -23
- package/src/constants.ts +0 -1
- package/src/env.d.ts +0 -10
- package/src/features/AgentWidget/index.tsx +0 -57
- package/src/global.d.ts +0 -1
- package/src/hooks/useAudioRecording.ts +0 -50
- package/src/hooks/useChat.ts +0 -262
- package/src/hooks/useChatData.tsx +0 -68
- package/src/hooks/useConfiguration.tsx +0 -56
- package/src/hooks/useScrollToBottom.ts +0 -31
- package/src/index.ts +0 -1
- package/src/models/FlowiseClient.ts +0 -103
- package/src/models.ts +0 -1
- package/src/register.tsx +0 -85
- package/src/services/apis.ts +0 -12
- package/src/services/bot.service.ts +0 -15
- package/src/services/chat.service.ts +0 -199
- package/src/types/bot.type.ts +0 -10
- package/src/types/chat.type.ts +0 -11
- package/src/types/common.type.ts +0 -17
- package/src/types/flowise.type.ts +0 -108
- package/src/types/user.type.ts +0 -15
- package/src/types.ts +0 -0
- package/src/utils/audioRecording.ts +0 -371
- package/src/utils/commonUtils.ts +0 -47
- package/src/utils/functionUtils.ts +0 -17
- package/src/utils/requestUtils.ts +0 -113
- package/src/utils/streamUtils.ts +0 -18
- package/src/web.ts +0 -6
- package/src/window.ts +0 -43
- package/tsconfig.json +0 -24
|
@@ -1,479 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
type ChangeEvent,
|
|
3
|
-
FC,
|
|
4
|
-
useCallback,
|
|
5
|
-
useEffect,
|
|
6
|
-
useRef,
|
|
7
|
-
useState,
|
|
8
|
-
} from 'react';
|
|
9
|
-
import { motion } from 'framer-motion';
|
|
10
|
-
import { useLocalStorage, useWindowSize } from 'usehooks-ts';
|
|
11
|
-
import {
|
|
12
|
-
cn,
|
|
13
|
-
generateExtendedFileName,
|
|
14
|
-
generateUUID,
|
|
15
|
-
} from '../../utils/commonUtils';
|
|
16
|
-
import { PreviewAttachment } from './PreviewAttachment';
|
|
17
|
-
import {
|
|
18
|
-
ArrowUpIcon,
|
|
19
|
-
CircleDotIcon,
|
|
20
|
-
MicrophoneIcon,
|
|
21
|
-
PlusIcon,
|
|
22
|
-
StopIcon,
|
|
23
|
-
TrashIcon,
|
|
24
|
-
VolumeIcon,
|
|
25
|
-
} from './Icons';
|
|
26
|
-
import { ChatMessageType, IFileUpload } from '../../types/flowise.type';
|
|
27
|
-
import { BotType } from '../../types/bot.type';
|
|
28
|
-
|
|
29
|
-
import { createAttachments } from '../../services/chat.service';
|
|
30
|
-
import { Button } from './ui/Button';
|
|
31
|
-
import { Textarea } from './ui/Textarea';
|
|
32
|
-
import { useAudioRecording } from '../../hooks/useAudioRecording';
|
|
33
|
-
import { useChatData } from '../../hooks/useChatData';
|
|
34
|
-
import { useConfiguration } from '../../hooks/useConfiguration';
|
|
35
|
-
import { SuggestionType } from 'types/common.type';
|
|
36
|
-
|
|
37
|
-
type PropsType = {
|
|
38
|
-
input: string;
|
|
39
|
-
setInput: (value: string) => void;
|
|
40
|
-
isLoading: boolean;
|
|
41
|
-
stop: () => void;
|
|
42
|
-
messages: ChatMessageType[];
|
|
43
|
-
setMessages: (messages: ChatMessageType[]) => void;
|
|
44
|
-
chatId: string;
|
|
45
|
-
handleSubmit: (
|
|
46
|
-
event?: { preventDefault?: () => void },
|
|
47
|
-
files?: IFileUpload[]
|
|
48
|
-
) => void;
|
|
49
|
-
className?: string;
|
|
50
|
-
append?: (message: ChatMessageType) => Promise<void>;
|
|
51
|
-
attachments?: IFileUpload[];
|
|
52
|
-
setAttachments?: (func: (files: IFileUpload[]) => IFileUpload[]) => void;
|
|
53
|
-
bot: BotType | null;
|
|
54
|
-
apiHost: string;
|
|
55
|
-
setEnableTTS: (value: boolean) => void;
|
|
56
|
-
enableTTS: boolean;
|
|
57
|
-
suggestedActions?: SuggestionType[];
|
|
58
|
-
};
|
|
59
|
-
|
|
60
|
-
export const MultimodalInput: FC<PropsType> = ({
|
|
61
|
-
input,
|
|
62
|
-
setInput,
|
|
63
|
-
isLoading,
|
|
64
|
-
stop,
|
|
65
|
-
messages,
|
|
66
|
-
setMessages,
|
|
67
|
-
chatId,
|
|
68
|
-
handleSubmit,
|
|
69
|
-
className,
|
|
70
|
-
append,
|
|
71
|
-
attachments,
|
|
72
|
-
setAttachments,
|
|
73
|
-
bot,
|
|
74
|
-
apiHost,
|
|
75
|
-
setEnableTTS,
|
|
76
|
-
enableTTS,
|
|
77
|
-
suggestedActions,
|
|
78
|
-
}) => {
|
|
79
|
-
const { theme } = useConfiguration();
|
|
80
|
-
const {
|
|
81
|
-
isRecording,
|
|
82
|
-
setIsRecording,
|
|
83
|
-
onRecordingCancelled,
|
|
84
|
-
onRecordingStopped,
|
|
85
|
-
elapsedTime,
|
|
86
|
-
isLoadingRecording,
|
|
87
|
-
} = useAudioRecording();
|
|
88
|
-
const textareaRef = useRef<HTMLTextAreaElement | null>(null);
|
|
89
|
-
const { width } = useWindowSize();
|
|
90
|
-
useEffect(() => {
|
|
91
|
-
if (textareaRef.current) {
|
|
92
|
-
adjustHeight();
|
|
93
|
-
}
|
|
94
|
-
}, []);
|
|
95
|
-
|
|
96
|
-
const adjustHeight = () => {
|
|
97
|
-
if (textareaRef.current) {
|
|
98
|
-
(textareaRef.current as HTMLTextAreaElement).style.height = 'auto';
|
|
99
|
-
(textareaRef.current as HTMLTextAreaElement).style.height =
|
|
100
|
-
`${textareaRef.current?.scrollHeight + 2}px`;
|
|
101
|
-
}
|
|
102
|
-
};
|
|
103
|
-
|
|
104
|
-
const [localStorageInput, setLocalStorageInput] = useLocalStorage(
|
|
105
|
-
'input',
|
|
106
|
-
''
|
|
107
|
-
);
|
|
108
|
-
|
|
109
|
-
useEffect(() => {
|
|
110
|
-
if (textareaRef.current) {
|
|
111
|
-
const domValue = textareaRef.current?.value;
|
|
112
|
-
// Prefer DOM value over localStorage to handle hydration
|
|
113
|
-
const finalValue = domValue || localStorageInput || '';
|
|
114
|
-
setInput(finalValue);
|
|
115
|
-
adjustHeight();
|
|
116
|
-
}
|
|
117
|
-
// Only run once after hydration
|
|
118
|
-
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
119
|
-
}, []);
|
|
120
|
-
|
|
121
|
-
useEffect(() => {
|
|
122
|
-
setLocalStorageInput(input);
|
|
123
|
-
}, [input, setLocalStorageInput]);
|
|
124
|
-
|
|
125
|
-
const handleInput = (event: ChangeEvent<HTMLTextAreaElement>) => {
|
|
126
|
-
setInput(event.target.value);
|
|
127
|
-
adjustHeight();
|
|
128
|
-
};
|
|
129
|
-
|
|
130
|
-
const fileInputRef = useRef<HTMLInputElement | null>(null);
|
|
131
|
-
const [uploadQueue, setUploadQueue] = useState<Array<string>>([]);
|
|
132
|
-
|
|
133
|
-
const submitForm = useCallback(async () => {
|
|
134
|
-
handleSubmit(undefined, attachments);
|
|
135
|
-
setLocalStorageInput('');
|
|
136
|
-
if (setAttachments) {
|
|
137
|
-
setAttachments((_) => []);
|
|
138
|
-
if (fileInputRef.current) {
|
|
139
|
-
(fileInputRef.current as HTMLInputElement).value = '';
|
|
140
|
-
}
|
|
141
|
-
}
|
|
142
|
-
if (width && width > 768) {
|
|
143
|
-
textareaRef.current?.focus();
|
|
144
|
-
}
|
|
145
|
-
}, [handleSubmit, setLocalStorageInput, width, attachments, chatId, bot]);
|
|
146
|
-
|
|
147
|
-
const uploadFile = useCallback(
|
|
148
|
-
async (file: File) => {
|
|
149
|
-
const formData = new FormData();
|
|
150
|
-
formData.append('file', file, generateExtendedFileName(file.name));
|
|
151
|
-
try {
|
|
152
|
-
return await createAttachments({
|
|
153
|
-
chatId,
|
|
154
|
-
apiHost,
|
|
155
|
-
body: formData,
|
|
156
|
-
});
|
|
157
|
-
} catch (error) {
|
|
158
|
-
console.error('Failed to upload file, please try again!');
|
|
159
|
-
}
|
|
160
|
-
},
|
|
161
|
-
[chatId]
|
|
162
|
-
);
|
|
163
|
-
|
|
164
|
-
const toAudioBase64 = (blob: Blob) => {
|
|
165
|
-
return new Promise<IFileUpload>((resolve) => {
|
|
166
|
-
let mimeType = '';
|
|
167
|
-
const pos = blob.type.indexOf(';');
|
|
168
|
-
if (pos === -1) {
|
|
169
|
-
mimeType = blob.type;
|
|
170
|
-
} else {
|
|
171
|
-
mimeType = blob.type.substring(0, pos);
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
// read blob and add to previews
|
|
175
|
-
const reader = new FileReader();
|
|
176
|
-
reader.readAsDataURL(blob);
|
|
177
|
-
reader.onloadend = () => {
|
|
178
|
-
const base64data = reader.result as string;
|
|
179
|
-
const upload: IFileUpload = {
|
|
180
|
-
tempId: generateUUID(),
|
|
181
|
-
data: base64data,
|
|
182
|
-
type: 'audio',
|
|
183
|
-
name: `audio_${Date.now()}.wav`,
|
|
184
|
-
mime: mimeType,
|
|
185
|
-
};
|
|
186
|
-
resolve(upload);
|
|
187
|
-
};
|
|
188
|
-
});
|
|
189
|
-
};
|
|
190
|
-
|
|
191
|
-
const toBase64 = async (
|
|
192
|
-
file: File,
|
|
193
|
-
type = 'file'
|
|
194
|
-
): Promise<IFileUpload | undefined> => {
|
|
195
|
-
return new Promise((resolve, reject) => {
|
|
196
|
-
const reader = new FileReader();
|
|
197
|
-
reader.readAsDataURL(file);
|
|
198
|
-
reader.onload = () => {
|
|
199
|
-
resolve({
|
|
200
|
-
tempId: generateUUID(),
|
|
201
|
-
data: reader.result as string,
|
|
202
|
-
type,
|
|
203
|
-
name: generateExtendedFileName(file.name),
|
|
204
|
-
mime: file.type,
|
|
205
|
-
});
|
|
206
|
-
};
|
|
207
|
-
reader.onerror = () => {
|
|
208
|
-
reject();
|
|
209
|
-
};
|
|
210
|
-
});
|
|
211
|
-
};
|
|
212
|
-
|
|
213
|
-
const checkUploadFile = useCallback(
|
|
214
|
-
async (file: File): Promise<IFileUpload | undefined> => {
|
|
215
|
-
if (file.type.startsWith('image')) {
|
|
216
|
-
return toBase64(file);
|
|
217
|
-
} else {
|
|
218
|
-
setUploadQueue([file.name]);
|
|
219
|
-
const response: any = await uploadFile(file);
|
|
220
|
-
if (response?.type == 'file:full') {
|
|
221
|
-
const f = response.result[0];
|
|
222
|
-
if (!f) {
|
|
223
|
-
return;
|
|
224
|
-
}
|
|
225
|
-
return {
|
|
226
|
-
tempId: generateUUID(),
|
|
227
|
-
data: f.content,
|
|
228
|
-
name: f.name,
|
|
229
|
-
mime: f.mimeType,
|
|
230
|
-
type: response?.type,
|
|
231
|
-
};
|
|
232
|
-
} else if (response?.type == 'file:rag') {
|
|
233
|
-
const { addedDocs } = response.result;
|
|
234
|
-
if (!addedDocs.length) {
|
|
235
|
-
return;
|
|
236
|
-
}
|
|
237
|
-
return toBase64(file, response?.type);
|
|
238
|
-
}
|
|
239
|
-
}
|
|
240
|
-
},
|
|
241
|
-
[setUploadQueue, uploadFile]
|
|
242
|
-
);
|
|
243
|
-
|
|
244
|
-
const handleFileChange = useCallback(
|
|
245
|
-
async (event: ChangeEvent<HTMLInputElement>) => {
|
|
246
|
-
const files = Array.from(event.target.files || []);
|
|
247
|
-
try {
|
|
248
|
-
const uploadPromises = files.map((file) => checkUploadFile(file));
|
|
249
|
-
const uploadedAttachments = await Promise.all(uploadPromises);
|
|
250
|
-
const successfullyUploadedAttachments = uploadedAttachments.filter(
|
|
251
|
-
(attachment) => attachment !== undefined
|
|
252
|
-
) as IFileUpload[];
|
|
253
|
-
if (setAttachments) {
|
|
254
|
-
setAttachments((currentAttachments: IFileUpload[]) => [
|
|
255
|
-
...currentAttachments,
|
|
256
|
-
...successfullyUploadedAttachments,
|
|
257
|
-
]);
|
|
258
|
-
}
|
|
259
|
-
} catch (error) {
|
|
260
|
-
console.error('Error uploading files!', error);
|
|
261
|
-
} finally {
|
|
262
|
-
setUploadQueue([]);
|
|
263
|
-
}
|
|
264
|
-
},
|
|
265
|
-
[setAttachments, checkUploadFile]
|
|
266
|
-
);
|
|
267
|
-
|
|
268
|
-
const handleSubmitRecording = useCallback(
|
|
269
|
-
async (blob: Blob) => {
|
|
270
|
-
try {
|
|
271
|
-
const audioFile = await toAudioBase64(blob);
|
|
272
|
-
handleSubmit(undefined, [audioFile]);
|
|
273
|
-
setIsRecording(false);
|
|
274
|
-
} catch (error) {
|
|
275
|
-
console.error('Error uploading files!', error);
|
|
276
|
-
}
|
|
277
|
-
},
|
|
278
|
-
[handleSubmit, setIsRecording]
|
|
279
|
-
);
|
|
280
|
-
|
|
281
|
-
const handleSend = useCallback(async () => {
|
|
282
|
-
if (isRecording) {
|
|
283
|
-
onRecordingStopped(handleSubmitRecording);
|
|
284
|
-
} else {
|
|
285
|
-
submitForm();
|
|
286
|
-
}
|
|
287
|
-
}, [submitForm, onRecordingStopped, handleSubmitRecording]);
|
|
288
|
-
|
|
289
|
-
return (
|
|
290
|
-
<div className="relative w-full flex flex-col gap-4">
|
|
291
|
-
{!!suggestedActions?.length && (
|
|
292
|
-
<div className="grid sm:grid-cols-2 gap-2 w-full">
|
|
293
|
-
{suggestedActions.map((suggestedAction, index) => (
|
|
294
|
-
<motion.div
|
|
295
|
-
initial={{ opacity: 0, y: 20 }}
|
|
296
|
-
animate={{ opacity: 1, y: 0 }}
|
|
297
|
-
exit={{ opacity: 0, y: 20 }}
|
|
298
|
-
transition={{ delay: 0.05 * index }}
|
|
299
|
-
key={`suggested-action-${suggestedAction.title}-${index}`}
|
|
300
|
-
className={index > 1 ? 'hidden sm:block' : 'block'}
|
|
301
|
-
>
|
|
302
|
-
<Button
|
|
303
|
-
variant="ghost"
|
|
304
|
-
onClick={(e) => {
|
|
305
|
-
e.preventDefault();
|
|
306
|
-
if (append) {
|
|
307
|
-
append({
|
|
308
|
-
role: 'apiMessage',
|
|
309
|
-
content: suggestedAction.action,
|
|
310
|
-
});
|
|
311
|
-
}
|
|
312
|
-
}}
|
|
313
|
-
className="text-left border rounded-xl px-4 py-3.5 text-sm flex-1 gap-1 sm:flex-col w-full h-auto justify-start items-start"
|
|
314
|
-
>
|
|
315
|
-
<span className="font-medium overflow-hidden whitespace-nowrap text-ellipsis w-full group-hover:overflow-visible group-hover:whitespace-normal">
|
|
316
|
-
{suggestedAction.title}
|
|
317
|
-
</span>
|
|
318
|
-
{!!suggestedAction.label && (
|
|
319
|
-
<span className="text-muted-foreground">
|
|
320
|
-
{suggestedAction.label}
|
|
321
|
-
</span>
|
|
322
|
-
)}
|
|
323
|
-
</Button>
|
|
324
|
-
</motion.div>
|
|
325
|
-
))}
|
|
326
|
-
</div>
|
|
327
|
-
)}
|
|
328
|
-
|
|
329
|
-
<input
|
|
330
|
-
type="file"
|
|
331
|
-
className="fixed -top-4 -left-4 size-0.5 opacity-0 pointer-events-none"
|
|
332
|
-
ref={fileInputRef}
|
|
333
|
-
multiple
|
|
334
|
-
onChange={handleFileChange}
|
|
335
|
-
tabIndex={-1}
|
|
336
|
-
/>
|
|
337
|
-
|
|
338
|
-
{((attachments && attachments.length > 0) || uploadQueue.length > 0) && (
|
|
339
|
-
<div className="flex flex-row gap-2 overflow-x-scroll items-end">
|
|
340
|
-
{attachments?.map((attachment) => (
|
|
341
|
-
<PreviewAttachment
|
|
342
|
-
key={attachment.tempId}
|
|
343
|
-
attachment={attachment}
|
|
344
|
-
/>
|
|
345
|
-
))}
|
|
346
|
-
|
|
347
|
-
{uploadQueue.map((filename) => (
|
|
348
|
-
<PreviewAttachment
|
|
349
|
-
key={filename}
|
|
350
|
-
attachment={{
|
|
351
|
-
data: '',
|
|
352
|
-
name: filename,
|
|
353
|
-
mime: '',
|
|
354
|
-
type: '',
|
|
355
|
-
}}
|
|
356
|
-
isUploading
|
|
357
|
-
/>
|
|
358
|
-
))}
|
|
359
|
-
</div>
|
|
360
|
-
)}
|
|
361
|
-
|
|
362
|
-
<Textarea
|
|
363
|
-
ref={textareaRef}
|
|
364
|
-
placeholder={theme?.input?.placeholder || 'Send a message...'}
|
|
365
|
-
value={input}
|
|
366
|
-
onChange={handleInput}
|
|
367
|
-
className={cn(
|
|
368
|
-
'min-h-[24px] max-h-[calc(75dvh)] overflow-hidden resize-none rounded-xl text-base bg-muted',
|
|
369
|
-
className
|
|
370
|
-
)}
|
|
371
|
-
rows={3}
|
|
372
|
-
autoFocus
|
|
373
|
-
onKeyDown={(event) => {
|
|
374
|
-
if (event.key === 'Enter' && !event.shiftKey) {
|
|
375
|
-
event.preventDefault();
|
|
376
|
-
if (isLoading) {
|
|
377
|
-
console.error(
|
|
378
|
-
'Please wait for the model to finish its response!'
|
|
379
|
-
);
|
|
380
|
-
} else if (uploadQueue.length) {
|
|
381
|
-
console.error('Please wait for file is uploading!');
|
|
382
|
-
} else {
|
|
383
|
-
handleSend();
|
|
384
|
-
}
|
|
385
|
-
}
|
|
386
|
-
}}
|
|
387
|
-
/>
|
|
388
|
-
|
|
389
|
-
{isLoading ? (
|
|
390
|
-
<Button
|
|
391
|
-
className="rounded-full p-1.5 h-fit absolute bottom-2 right-2 m-0.5 border dark:border-zinc-600"
|
|
392
|
-
onClick={(event) => {
|
|
393
|
-
event.preventDefault();
|
|
394
|
-
stop();
|
|
395
|
-
setMessages(messages);
|
|
396
|
-
}}
|
|
397
|
-
>
|
|
398
|
-
<StopIcon size={14} />
|
|
399
|
-
</Button>
|
|
400
|
-
) : (
|
|
401
|
-
<Button
|
|
402
|
-
className="rounded-full p-1.5 h-fit absolute bottom-2 right-2 m-0.5 border dark:border-zinc-600"
|
|
403
|
-
onClick={(event) => {
|
|
404
|
-
event.preventDefault();
|
|
405
|
-
handleSend();
|
|
406
|
-
}}
|
|
407
|
-
disabled={
|
|
408
|
-
!isRecording && (input.length === 0 || !!uploadQueue.length)
|
|
409
|
-
} // zero input or uploading
|
|
410
|
-
>
|
|
411
|
-
<ArrowUpIcon size={14} />
|
|
412
|
-
</Button>
|
|
413
|
-
)}
|
|
414
|
-
{isRecording ? (
|
|
415
|
-
<>
|
|
416
|
-
<div
|
|
417
|
-
className="rounded-full bg-background flex absolute p-1 bottom-2 right-[80px]"
|
|
418
|
-
data-testid="input"
|
|
419
|
-
>
|
|
420
|
-
<div className="flex items-center gap-3">
|
|
421
|
-
<span>
|
|
422
|
-
<CircleDotIcon color="red" />
|
|
423
|
-
</span>
|
|
424
|
-
<span>{elapsedTime || '00:00'}</span>
|
|
425
|
-
{isLoadingRecording && <span className="ml-1.5">Sending...</span>}
|
|
426
|
-
</div>
|
|
427
|
-
</div>
|
|
428
|
-
<Button
|
|
429
|
-
className="rounded-full p-1.5 h-fit absolute bottom-2 right-11 m-0.5 dark:border-zinc-700"
|
|
430
|
-
variant="outline"
|
|
431
|
-
onClick={(event) => {
|
|
432
|
-
event.preventDefault();
|
|
433
|
-
onRecordingCancelled();
|
|
434
|
-
}}
|
|
435
|
-
>
|
|
436
|
-
<TrashIcon size={14} color="red" />
|
|
437
|
-
</Button>
|
|
438
|
-
</>
|
|
439
|
-
) : (
|
|
440
|
-
<>
|
|
441
|
-
<Button
|
|
442
|
-
className="rounded-full p-1.5 h-fit absolute bottom-2 right-11 m-0.5 dark:border-zinc-700"
|
|
443
|
-
onClick={(event) => {
|
|
444
|
-
event.preventDefault();
|
|
445
|
-
setIsRecording(true);
|
|
446
|
-
}}
|
|
447
|
-
variant="outline"
|
|
448
|
-
disabled={isLoading}
|
|
449
|
-
>
|
|
450
|
-
<MicrophoneIcon size={14} />
|
|
451
|
-
</Button>
|
|
452
|
-
<Button
|
|
453
|
-
className={`rounded-full p-1.5 h-fit absolute bottom-2 right-[80px] m-0.5 dark:border-zinc-700 ${enableTTS ? 'text-white hover:bg-primary/90 bg-primary' : ''}`}
|
|
454
|
-
onClick={(event) => {
|
|
455
|
-
event.preventDefault();
|
|
456
|
-
setEnableTTS(!enableTTS);
|
|
457
|
-
}}
|
|
458
|
-
variant="outline"
|
|
459
|
-
disabled={isLoading}
|
|
460
|
-
>
|
|
461
|
-
<VolumeIcon />
|
|
462
|
-
</Button>
|
|
463
|
-
</>
|
|
464
|
-
)}
|
|
465
|
-
|
|
466
|
-
<Button
|
|
467
|
-
className="rounded-full p-1.5 h-fit absolute bottom-2 left-2 m-0.5 dark:border-zinc-700"
|
|
468
|
-
onClick={(event) => {
|
|
469
|
-
event.preventDefault();
|
|
470
|
-
fileInputRef.current?.click();
|
|
471
|
-
}}
|
|
472
|
-
variant="outline"
|
|
473
|
-
disabled={isLoading || isRecording}
|
|
474
|
-
>
|
|
475
|
-
<PlusIcon size={14} />
|
|
476
|
-
</Button>
|
|
477
|
-
</div>
|
|
478
|
-
);
|
|
479
|
-
};
|
|
@@ -1,46 +0,0 @@
|
|
|
1
|
-
import { motion } from 'framer-motion';
|
|
2
|
-
import { FC } from 'react';
|
|
3
|
-
import { BotType } from '../../types/bot.type';
|
|
4
|
-
import { useConfiguration } from '../../hooks/useConfiguration';
|
|
5
|
-
|
|
6
|
-
type PropsType = {
|
|
7
|
-
bot: BotType | null;
|
|
8
|
-
};
|
|
9
|
-
export const Overview: FC<PropsType> = ({ bot }: PropsType) => {
|
|
10
|
-
const { theme } = useConfiguration();
|
|
11
|
-
return (
|
|
12
|
-
<motion.div
|
|
13
|
-
key="overview"
|
|
14
|
-
className="max-w-3xl m-auto md:mt-20"
|
|
15
|
-
initial={{ opacity: 0, scale: 0.98 }}
|
|
16
|
-
animate={{ opacity: 1, scale: 1 }}
|
|
17
|
-
exit={{ opacity: 0, scale: 0.98 }}
|
|
18
|
-
transition={{ delay: 0.5 }}
|
|
19
|
-
>
|
|
20
|
-
<div className="rounded-xl p-6 flex flex-col gap-2 leading-relaxed text-center max-w-xl">
|
|
21
|
-
<p className="flex flex-row justify-center gap-4 items-center"></p>
|
|
22
|
-
{!theme?.overview ? (
|
|
23
|
-
<>
|
|
24
|
-
{bot?.avatar && (
|
|
25
|
-
<img
|
|
26
|
-
src={bot?.avatar}
|
|
27
|
-
alt={bot?.name ?? 'Avatar'}
|
|
28
|
-
width={40}
|
|
29
|
-
height={40}
|
|
30
|
-
className="rounded-full m-auto"
|
|
31
|
-
/>
|
|
32
|
-
)}
|
|
33
|
-
<p className="font-semibold text-xl">{bot?.name}</p>
|
|
34
|
-
</>
|
|
35
|
-
) : (
|
|
36
|
-
<>
|
|
37
|
-
<p className="font-semibold text-xl">{theme?.overview?.title}</p>
|
|
38
|
-
<p>{theme?.overview?.description}</p>
|
|
39
|
-
</>
|
|
40
|
-
)}
|
|
41
|
-
</div>
|
|
42
|
-
</motion.div>
|
|
43
|
-
);
|
|
44
|
-
};
|
|
45
|
-
|
|
46
|
-
export default Overview;
|
|
@@ -1,46 +0,0 @@
|
|
|
1
|
-
import { IFileUpload } from '../../types/flowise.type';
|
|
2
|
-
import { LoaderIcon } from './Icons';
|
|
3
|
-
import React from 'react';
|
|
4
|
-
|
|
5
|
-
export const PreviewAttachment = ({
|
|
6
|
-
attachment,
|
|
7
|
-
isUploading = false,
|
|
8
|
-
}: {
|
|
9
|
-
attachment: IFileUpload;
|
|
10
|
-
isUploading?: boolean;
|
|
11
|
-
}) => {
|
|
12
|
-
const { name, data, mime, tempId } = attachment;
|
|
13
|
-
return (
|
|
14
|
-
<div className="flex flex-col gap-2">
|
|
15
|
-
<div className="w-30 p-0 max-w-[400px] bg-muted rounded-md relative flex flex-col items-center justify-center">
|
|
16
|
-
{data ? (
|
|
17
|
-
mime.startsWith('audio') ? (
|
|
18
|
-
<audio controls>
|
|
19
|
-
<source src={data} type={mime} />
|
|
20
|
-
</audio>
|
|
21
|
-
) : mime.startsWith('image') ? (
|
|
22
|
-
<img
|
|
23
|
-
key={tempId}
|
|
24
|
-
src={data}
|
|
25
|
-
alt={name ?? 'An image attachment'}
|
|
26
|
-
className="rounded-md size-full object-contain"
|
|
27
|
-
/>
|
|
28
|
-
) : (
|
|
29
|
-
<div className="w-full text-left truncate">{name}</div>
|
|
30
|
-
)
|
|
31
|
-
) : (
|
|
32
|
-
<div className="w-full text-left truncate">{name}</div>
|
|
33
|
-
)}
|
|
34
|
-
|
|
35
|
-
{isUploading && (
|
|
36
|
-
<div className="animate-spin absolute text-zinc-500">
|
|
37
|
-
<LoaderIcon />
|
|
38
|
-
</div>
|
|
39
|
-
)}
|
|
40
|
-
</div>
|
|
41
|
-
{/*<div className="text-xs text-zinc-500 max-w-[200px] truncate ">*/}
|
|
42
|
-
{/* {name}*/}
|
|
43
|
-
{/*</div>*/}
|
|
44
|
-
</div>
|
|
45
|
-
);
|
|
46
|
-
};
|
|
@@ -1,55 +0,0 @@
|
|
|
1
|
-
import { Slot } from '@radix-ui/react-slot';
|
|
2
|
-
import { cn } from '../../../utils/commonUtils';
|
|
3
|
-
import { cva, type VariantProps } from 'class-variance-authority';
|
|
4
|
-
import { ButtonHTMLAttributes, forwardRef } from 'react';
|
|
5
|
-
|
|
6
|
-
const buttonVariants = cva(
|
|
7
|
-
'inline-flex items-center gap-2 justify-center whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0',
|
|
8
|
-
{
|
|
9
|
-
variants: {
|
|
10
|
-
variant: {
|
|
11
|
-
default: 'bg-primary text-primary-foreground hover:bg-primary/90',
|
|
12
|
-
destructive:
|
|
13
|
-
'bg-destructive text-destructive-foreground hover:bg-destructive/90',
|
|
14
|
-
outline:
|
|
15
|
-
'border border-input bg-background hover:bg-accent hover:text-accent-foreground',
|
|
16
|
-
secondary:
|
|
17
|
-
'bg-secondary text-secondary-foreground hover:bg-secondary/80',
|
|
18
|
-
ghost: 'hover:bg-accent hover:text-accent-foreground',
|
|
19
|
-
link: 'text-primary underline-offset-4 hover:underline',
|
|
20
|
-
},
|
|
21
|
-
size: {
|
|
22
|
-
default: 'h-10 px-4 py-2',
|
|
23
|
-
sm: 'h-9 rounded-md px-3',
|
|
24
|
-
lg: 'h-11 rounded-md px-8',
|
|
25
|
-
icon: 'h-10 w-10',
|
|
26
|
-
},
|
|
27
|
-
},
|
|
28
|
-
defaultVariants: {
|
|
29
|
-
variant: 'default',
|
|
30
|
-
size: 'default',
|
|
31
|
-
},
|
|
32
|
-
}
|
|
33
|
-
);
|
|
34
|
-
|
|
35
|
-
export interface ButtonProps
|
|
36
|
-
extends ButtonHTMLAttributes<HTMLButtonElement>,
|
|
37
|
-
VariantProps<typeof buttonVariants> {
|
|
38
|
-
asChild?: boolean;
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
const Button = forwardRef<HTMLButtonElement, ButtonProps>(
|
|
42
|
-
({ className, variant, size, asChild = false, ...props }, ref) => {
|
|
43
|
-
const Comp = asChild ? Slot : 'button';
|
|
44
|
-
return (
|
|
45
|
-
<Comp
|
|
46
|
-
className={cn(buttonVariants({ variant, size, className }))}
|
|
47
|
-
ref={ref}
|
|
48
|
-
{...props}
|
|
49
|
-
/>
|
|
50
|
-
);
|
|
51
|
-
}
|
|
52
|
-
);
|
|
53
|
-
Button.displayName = 'Button';
|
|
54
|
-
|
|
55
|
-
export { Button, buttonVariants };
|
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
import { cn } from '../../../utils/commonUtils';
|
|
2
|
-
import { forwardRef, TextareaHTMLAttributes } from 'react';
|
|
3
|
-
|
|
4
|
-
export interface TextareaProps
|
|
5
|
-
extends TextareaHTMLAttributes<HTMLTextAreaElement> {}
|
|
6
|
-
|
|
7
|
-
const Textarea = forwardRef<HTMLTextAreaElement, TextareaProps>(
|
|
8
|
-
({ className, ...props }, ref) => {
|
|
9
|
-
return (
|
|
10
|
-
<textarea
|
|
11
|
-
className={cn(
|
|
12
|
-
'flex min-h-[80px] w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50',
|
|
13
|
-
className
|
|
14
|
-
)}
|
|
15
|
-
ref={ref}
|
|
16
|
-
{...props}
|
|
17
|
-
/>
|
|
18
|
-
);
|
|
19
|
-
}
|
|
20
|
-
);
|
|
21
|
-
Textarea.displayName = 'Textarea';
|
|
22
|
-
|
|
23
|
-
export { Textarea };
|
package/src/constants.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export const agentWidgetElementName = 'clik-agent-widget';
|