@clikvn/agent-widget-embedded 0.0.1-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.
Files changed (130) hide show
  1. package/.eslintrc +34 -0
  2. package/.prettierrc +8 -0
  3. package/README.md +20 -0
  4. package/base.json +21 -0
  5. package/dist/commons/constants/index.d.ts +2 -0
  6. package/dist/commons/constants/index.d.ts.map +1 -0
  7. package/dist/commons/constants/variables.d.ts +5 -0
  8. package/dist/commons/constants/variables.d.ts.map +1 -0
  9. package/dist/components/Agent/index.d.ts +3 -0
  10. package/dist/components/Agent/index.d.ts.map +1 -0
  11. package/dist/components/Chat/Chat.d.ts +10 -0
  12. package/dist/components/Chat/Chat.d.ts.map +1 -0
  13. package/dist/components/Chat/Icons.d.ts +120 -0
  14. package/dist/components/Chat/Icons.d.ts.map +1 -0
  15. package/dist/components/Chat/Markdown.d.ts +7 -0
  16. package/dist/components/Chat/Markdown.d.ts.map +1 -0
  17. package/dist/components/Chat/Message.d.ts +15 -0
  18. package/dist/components/Chat/Message.d.ts.map +1 -0
  19. package/dist/components/Chat/MultimodalInput.d.ts +24 -0
  20. package/dist/components/Chat/MultimodalInput.d.ts.map +1 -0
  21. package/dist/components/Chat/Overview.d.ts +8 -0
  22. package/dist/components/Chat/Overview.d.ts.map +1 -0
  23. package/dist/components/Chat/PreviewAttachment.d.ts +6 -0
  24. package/dist/components/Chat/PreviewAttachment.d.ts.map +1 -0
  25. package/dist/components/Chat/ui/Button.d.ts +12 -0
  26. package/dist/components/Chat/ui/Button.d.ts.map +1 -0
  27. package/dist/components/Chat/ui/Textarea.d.ts +6 -0
  28. package/dist/components/Chat/ui/Textarea.d.ts.map +1 -0
  29. package/dist/constants.d.ts +2 -0
  30. package/dist/constants.d.ts.map +1 -0
  31. package/dist/features/AgentWidget/index.d.ts +16 -0
  32. package/dist/features/AgentWidget/index.d.ts.map +1 -0
  33. package/dist/hooks/useChat.d.ts +25 -0
  34. package/dist/hooks/useChat.d.ts.map +1 -0
  35. package/dist/hooks/useChatData.d.ts +18 -0
  36. package/dist/hooks/useChatData.d.ts.map +1 -0
  37. package/dist/hooks/useConfiguration.d.ts +20 -0
  38. package/dist/hooks/useConfiguration.d.ts.map +1 -0
  39. package/dist/hooks/useConnection.d.ts +15 -0
  40. package/dist/hooks/useConnection.d.ts.map +1 -0
  41. package/dist/hooks/useScrollToBottom.d.ts +6 -0
  42. package/dist/hooks/useScrollToBottom.d.ts.map +1 -0
  43. package/dist/index.d.ts +2 -0
  44. package/dist/index.d.ts.map +1 -0
  45. package/dist/models/FlowiseClient.d.ts +20 -0
  46. package/dist/models/FlowiseClient.d.ts.map +1 -0
  47. package/dist/models.d.ts +2 -0
  48. package/dist/models.d.ts.map +1 -0
  49. package/dist/register.d.ts +30 -0
  50. package/dist/register.d.ts.map +1 -0
  51. package/dist/services/apis.d.ts +7 -0
  52. package/dist/services/apis.d.ts.map +1 -0
  53. package/dist/services/bot.service.d.ts +3 -0
  54. package/dist/services/bot.service.d.ts.map +1 -0
  55. package/dist/services/chat.service.d.ts +32 -0
  56. package/dist/services/chat.service.d.ts.map +1 -0
  57. package/dist/services/user.service.d.ts +3 -0
  58. package/dist/services/user.service.d.ts.map +1 -0
  59. package/dist/types/agentType.d.ts +11 -0
  60. package/dist/types/agentType.d.ts.map +1 -0
  61. package/dist/types/bot.type.d.ts +11 -0
  62. package/dist/types/bot.type.d.ts.map +1 -0
  63. package/dist/types/chat.type.d.ts +10 -0
  64. package/dist/types/chat.type.d.ts.map +1 -0
  65. package/dist/types/common.type.d.ts +11 -0
  66. package/dist/types/common.type.d.ts.map +1 -0
  67. package/dist/types/flowise.type.d.ts +90 -0
  68. package/dist/types/flowise.type.d.ts.map +1 -0
  69. package/dist/types/user.type.d.ts +14 -0
  70. package/dist/types/user.type.d.ts.map +1 -0
  71. package/dist/types.d.ts +1 -0
  72. package/dist/types.d.ts.map +1 -0
  73. package/dist/utils/commonUtils.d.ts +7 -0
  74. package/dist/utils/commonUtils.d.ts.map +1 -0
  75. package/dist/utils/functionUtils.d.ts +3 -0
  76. package/dist/utils/functionUtils.d.ts.map +1 -0
  77. package/dist/utils/requestUtils.d.ts +16 -0
  78. package/dist/utils/requestUtils.d.ts.map +1 -0
  79. package/dist/utils/streamUtils.d.ts +5 -0
  80. package/dist/utils/streamUtils.d.ts.map +1 -0
  81. package/dist/web.d.ts +18 -0
  82. package/dist/web.d.ts.map +1 -0
  83. package/dist/web.js +1 -0
  84. package/dist/window.d.ts +29 -0
  85. package/dist/window.d.ts.map +1 -0
  86. package/package.json +91 -0
  87. package/rollup.config.js +56 -0
  88. package/src/assets/common.css +148 -0
  89. package/src/assets/tailwindcss.css +3 -0
  90. package/src/commons/constants/index.ts +1 -0
  91. package/src/commons/constants/variables.ts +20 -0
  92. package/src/components/Agent/index.tsx +14 -0
  93. package/src/components/Chat/Chat.tsx +84 -0
  94. package/src/components/Chat/Icons.tsx +883 -0
  95. package/src/components/Chat/Markdown.tsx +324 -0
  96. package/src/components/Chat/Message.tsx +185 -0
  97. package/src/components/Chat/MultimodalInput.tsx +371 -0
  98. package/src/components/Chat/Overview.tsx +47 -0
  99. package/src/components/Chat/PreviewAttachment.tsx +41 -0
  100. package/src/components/Chat/ui/Button.tsx +55 -0
  101. package/src/components/Chat/ui/Textarea.tsx +23 -0
  102. package/src/constants.ts +1 -0
  103. package/src/env.d.ts +10 -0
  104. package/src/features/AgentWidget/index.tsx +47 -0
  105. package/src/global.d.ts +1 -0
  106. package/src/hooks/useChat.ts +225 -0
  107. package/src/hooks/useChatData.tsx +68 -0
  108. package/src/hooks/useConfiguration.tsx +54 -0
  109. package/src/hooks/useScrollToBottom.ts +31 -0
  110. package/src/index.ts +1 -0
  111. package/src/models/FlowiseClient.ts +103 -0
  112. package/src/models.ts +1 -0
  113. package/src/register.tsx +66 -0
  114. package/src/services/apis.ts +10 -0
  115. package/src/services/bot.service.ts +15 -0
  116. package/src/services/chat.service.ts +164 -0
  117. package/src/types/bot.type.ts +10 -0
  118. package/src/types/chat.type.ts +11 -0
  119. package/src/types/common.type.ts +11 -0
  120. package/src/types/flowise.type.ts +99 -0
  121. package/src/types/user.type.ts +15 -0
  122. package/src/types.ts +0 -0
  123. package/src/utils/commonUtils.ts +47 -0
  124. package/src/utils/functionUtils.ts +17 -0
  125. package/src/utils/requestUtils.ts +113 -0
  126. package/src/utils/streamUtils.ts +18 -0
  127. package/src/web.ts +6 -0
  128. package/src/window.ts +55 -0
  129. package/tailwind.config.cjs +122 -0
  130. package/tsconfig.json +24 -0
@@ -0,0 +1,371 @@
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 { ArrowUpIcon, PaperclipIcon, StopIcon } from './Icons';
18
+ import { ChatMessageType, IFileUpload } from '../../types/flowise.type';
19
+ import { BotType } from '../../types/bot.type';
20
+
21
+ import { createAttachments } from '../../services/chat.service';
22
+ import { Button } from './ui/Button';
23
+ import { Textarea } from './ui/Textarea';
24
+
25
+ const suggestedActions = [
26
+ {
27
+ title: 'What is the weather',
28
+ label: 'in Ha Noi?',
29
+ action: 'What is the weather in Ha Noi?',
30
+ },
31
+ {
32
+ title: 'Create a travel plan for an traveling',
33
+ label: 'to Ha Noi',
34
+ action: 'Create a travel plan for an traveling to Ha Noi',
35
+ },
36
+ {
37
+ title: 'Top of tourist attractions',
38
+ label: 'in Ha Noi',
39
+ action: 'Top of tourist attractions in Ha Noi',
40
+ },
41
+ {
42
+ title: 'List of museums',
43
+ label: 'in Ha Noi',
44
+ action: 'List of museums in Ha Noi',
45
+ },
46
+ ];
47
+
48
+ type PropsType = {
49
+ input: string;
50
+ setInput: (value: string) => void;
51
+ isLoading: boolean;
52
+ stop: () => void;
53
+ messages: ChatMessageType[];
54
+ setMessages: (messages: ChatMessageType[]) => void;
55
+ chatId: string;
56
+ handleSubmit: (
57
+ event?: { preventDefault?: () => void },
58
+ files?: IFileUpload[]
59
+ ) => void;
60
+ className?: string;
61
+ append?: (message: ChatMessageType) => Promise<void>;
62
+ attachments?: IFileUpload[];
63
+ setAttachments?: (func: (files: IFileUpload[]) => IFileUpload[]) => void;
64
+ bot: BotType | null;
65
+ apiHost: string;
66
+ };
67
+
68
+ export const MultimodalInput: FC<PropsType> = ({
69
+ input,
70
+ setInput,
71
+ isLoading,
72
+ stop,
73
+ messages,
74
+ setMessages,
75
+ chatId,
76
+ handleSubmit,
77
+ className,
78
+ append,
79
+ attachments,
80
+ setAttachments,
81
+ bot,
82
+ apiHost,
83
+ }) => {
84
+ const textareaRef = useRef<HTMLTextAreaElement | null>(null);
85
+ const { width } = useWindowSize();
86
+ useEffect(() => {
87
+ if (textareaRef.current) {
88
+ adjustHeight();
89
+ }
90
+ }, []);
91
+
92
+ const adjustHeight = () => {
93
+ if (textareaRef.current) {
94
+ (textareaRef.current as HTMLTextAreaElement).style.height = 'auto';
95
+ (textareaRef.current as HTMLTextAreaElement).style.height =
96
+ `${textareaRef.current?.scrollHeight + 2}px`;
97
+ }
98
+ };
99
+
100
+ const [localStorageInput, setLocalStorageInput] = useLocalStorage(
101
+ 'input',
102
+ ''
103
+ );
104
+
105
+ useEffect(() => {
106
+ if (textareaRef.current) {
107
+ const domValue = textareaRef.current?.value;
108
+ // Prefer DOM value over localStorage to handle hydration
109
+ const finalValue = domValue || localStorageInput || '';
110
+ setInput(finalValue);
111
+ adjustHeight();
112
+ }
113
+ // Only run once after hydration
114
+ // eslint-disable-next-line react-hooks/exhaustive-deps
115
+ }, []);
116
+
117
+ useEffect(() => {
118
+ setLocalStorageInput(input);
119
+ }, [input, setLocalStorageInput]);
120
+
121
+ const handleInput = (event: ChangeEvent<HTMLTextAreaElement>) => {
122
+ setInput(event.target.value);
123
+ adjustHeight();
124
+ };
125
+
126
+ const fileInputRef = useRef<HTMLInputElement | null>(null);
127
+ const [uploadQueue, setUploadQueue] = useState<Array<string>>([]);
128
+
129
+ const submitForm = useCallback(async () => {
130
+ handleSubmit(undefined, attachments);
131
+ setLocalStorageInput('');
132
+ if (setAttachments) {
133
+ setAttachments((currentAttachments: IFileUpload[]) => []);
134
+ if (fileInputRef.current) {
135
+ (fileInputRef.current as HTMLInputElement).value = '';
136
+ }
137
+ }
138
+ if (width && width > 768) {
139
+ textareaRef.current?.focus();
140
+ }
141
+ }, [handleSubmit, setLocalStorageInput, width, attachments, chatId, bot]);
142
+
143
+ const uploadFile = useCallback(
144
+ async (file: File) => {
145
+ const formData = new FormData();
146
+ formData.append('file', file, generateExtendedFileName(file.name));
147
+ try {
148
+ return await createAttachments({
149
+ chatId,
150
+ apiHost,
151
+ body: formData,
152
+ });
153
+ } catch (error) {
154
+ console.error('Failed to upload file, please try again!');
155
+ }
156
+ },
157
+ [chatId]
158
+ );
159
+
160
+ const toBase64 = async (
161
+ file: File,
162
+ type = 'file'
163
+ ): Promise<IFileUpload | undefined> => {
164
+ return new Promise((resolve, reject) => {
165
+ const reader = new FileReader();
166
+ reader.readAsDataURL(file);
167
+ reader.onload = () => {
168
+ resolve({
169
+ tempId: generateUUID(),
170
+ data: reader.result as string,
171
+ type,
172
+ name: generateExtendedFileName(file.name),
173
+ mime: file.type,
174
+ });
175
+ };
176
+ reader.onerror = () => {
177
+ reject();
178
+ };
179
+ });
180
+ };
181
+
182
+ const checkUploadFile = useCallback(
183
+ async (file: File): Promise<IFileUpload | undefined> => {
184
+ if (file.type.startsWith('image')) {
185
+ return toBase64(file);
186
+ } else {
187
+ setUploadQueue([file.name]);
188
+ const response: any = await uploadFile(file);
189
+ if (response?.type == 'file:full') {
190
+ const f = response.result[0];
191
+ if (!f) {
192
+ return;
193
+ }
194
+ return {
195
+ tempId: generateUUID(),
196
+ data: f.content,
197
+ name: f.name,
198
+ mime: f.mimeType,
199
+ type: response?.type,
200
+ };
201
+ } else if (response?.type == 'file:rag') {
202
+ const { addedDocs } = response.result;
203
+ if (!addedDocs.length) {
204
+ return;
205
+ }
206
+ return toBase64(file, response?.type);
207
+ }
208
+ }
209
+ },
210
+ [setUploadQueue, uploadFile]
211
+ );
212
+
213
+ const handleFileChange = useCallback(
214
+ async (event: ChangeEvent<HTMLInputElement>) => {
215
+ const files = Array.from(event.target.files || []);
216
+
217
+ try {
218
+ const uploadPromises = files.map((file) => checkUploadFile(file));
219
+ const uploadedAttachments = await Promise.all(uploadPromises);
220
+ const successfullyUploadedAttachments = uploadedAttachments.filter(
221
+ (attachment) => attachment !== undefined
222
+ ) as IFileUpload[];
223
+ if (setAttachments) {
224
+ setAttachments((currentAttachments: IFileUpload[]) => [
225
+ ...currentAttachments,
226
+ ...successfullyUploadedAttachments,
227
+ ]);
228
+ }
229
+ } catch (error) {
230
+ console.error('Error uploading files!', error);
231
+ } finally {
232
+ setUploadQueue([]);
233
+ }
234
+ },
235
+ [setAttachments, checkUploadFile]
236
+ );
237
+
238
+ return (
239
+ <div className="relative w-full flex flex-col gap-4">
240
+ {messages.length === 0 && (
241
+ <div className="grid sm:grid-cols-2 gap-2 w-full">
242
+ {suggestedActions.map((suggestedAction, index) => (
243
+ <motion.div
244
+ initial={{ opacity: 0, y: 20 }}
245
+ animate={{ opacity: 1, y: 0 }}
246
+ exit={{ opacity: 0, y: 20 }}
247
+ transition={{ delay: 0.05 * index }}
248
+ key={`suggested-action-${suggestedAction.title}-${index}`}
249
+ className={index > 1 ? 'hidden sm:block' : 'block'}
250
+ >
251
+ <Button
252
+ variant="ghost"
253
+ onClick={async () => {
254
+ if (append) {
255
+ append({
256
+ role: 'apiMessage',
257
+ content: suggestedAction.action,
258
+ });
259
+ }
260
+ }}
261
+ 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"
262
+ >
263
+ <span className="font-medium">{suggestedAction.title}</span>
264
+ <span className="text-muted-foreground">
265
+ {suggestedAction.label}
266
+ </span>
267
+ </Button>
268
+ </motion.div>
269
+ ))}
270
+ </div>
271
+ )}
272
+
273
+ <input
274
+ type="file"
275
+ className="fixed -top-4 -left-4 size-0.5 opacity-0 pointer-events-none"
276
+ ref={fileInputRef}
277
+ multiple
278
+ onChange={handleFileChange}
279
+ tabIndex={-1}
280
+ />
281
+
282
+ {((attachments && attachments.length > 0) || uploadQueue.length > 0) && (
283
+ <div className="flex flex-row gap-2 overflow-x-scroll items-end">
284
+ {attachments?.map((attachment) => (
285
+ <PreviewAttachment
286
+ key={attachment.tempId}
287
+ attachment={attachment}
288
+ />
289
+ ))}
290
+
291
+ {uploadQueue.map((filename) => (
292
+ <PreviewAttachment
293
+ key={filename}
294
+ attachment={{
295
+ data: '',
296
+ name: filename,
297
+ mime: '',
298
+ type: '',
299
+ }}
300
+ isUploading
301
+ />
302
+ ))}
303
+ </div>
304
+ )}
305
+
306
+ <Textarea
307
+ ref={textareaRef}
308
+ placeholder="Send a message..."
309
+ value={input}
310
+ onChange={handleInput}
311
+ className={cn(
312
+ 'min-h-[24px] max-h-[calc(75dvh)] overflow-hidden resize-none rounded-xl text-base bg-muted',
313
+ className
314
+ )}
315
+ rows={3}
316
+ autoFocus
317
+ onKeyDown={(event) => {
318
+ if (event.key === 'Enter' && !event.shiftKey) {
319
+ event.preventDefault();
320
+
321
+ if (isLoading) {
322
+ console.error(
323
+ 'Please wait for the model to finish its response!'
324
+ );
325
+ } else if (uploadQueue.length) {
326
+ console.error('Please wait for file is uploading!');
327
+ } else {
328
+ submitForm();
329
+ }
330
+ }
331
+ }}
332
+ />
333
+
334
+ {isLoading ? (
335
+ <Button
336
+ className="rounded-full p-1.5 h-fit absolute bottom-2 right-2 m-0.5 border dark:border-zinc-600"
337
+ onClick={(event) => {
338
+ event.preventDefault();
339
+ stop();
340
+ setMessages(messages);
341
+ }}
342
+ >
343
+ <StopIcon size={14} />
344
+ </Button>
345
+ ) : (
346
+ <Button
347
+ className="rounded-full p-1.5 h-fit absolute bottom-2 right-2 m-0.5 border dark:border-zinc-600"
348
+ onClick={(event) => {
349
+ event.preventDefault();
350
+ submitForm();
351
+ }}
352
+ disabled={input.length === 0 || !!uploadQueue.length}
353
+ >
354
+ <ArrowUpIcon size={14} />
355
+ </Button>
356
+ )}
357
+
358
+ <Button
359
+ className="rounded-full p-1.5 h-fit absolute bottom-2 right-11 m-0.5 dark:border-zinc-700"
360
+ onClick={(event) => {
361
+ event.preventDefault();
362
+ fileInputRef.current?.click();
363
+ }}
364
+ variant="outline"
365
+ disabled={isLoading}
366
+ >
367
+ <PaperclipIcon size={14} />
368
+ </Button>
369
+ </div>
370
+ );
371
+ };
@@ -0,0 +1,47 @@
1
+ import { motion } from 'framer-motion';
2
+ import { FC } from 'react';
3
+ import { BotType } from '../../types/bot.type';
4
+
5
+ type PropsType = {
6
+ bot: BotType | null;
7
+ };
8
+ export const Overview: FC<PropsType> = ({ bot }: PropsType) => {
9
+ return (
10
+ <motion.div
11
+ key="overview"
12
+ className="max-w-3xl mx-auto md:mt-20"
13
+ initial={{ opacity: 0, scale: 0.98 }}
14
+ animate={{ opacity: 1, scale: 1 }}
15
+ exit={{ opacity: 0, scale: 0.98 }}
16
+ transition={{ delay: 0.5 }}
17
+ >
18
+ <div className="rounded-xl p-6 flex flex-col gap-2 leading-relaxed text-center max-w-xl">
19
+ <p className="flex flex-row justify-center gap-4 items-center"></p>
20
+ {bot?.avatar && (
21
+ <img
22
+ src={bot?.avatar}
23
+ alt={bot?.name ?? 'Avatar'}
24
+ width={40}
25
+ height={40}
26
+ className="rounded-full m-auto"
27
+ />
28
+ )}
29
+
30
+ <p className="font-semibold text-xl">{`${bot?.name}`}</p>
31
+ <p>
32
+ {/* You can learn more about the AI by visiting the{' '}*/}
33
+ {/* <Link*/}
34
+ {/* className="font-medium underline underline-offset-4"*/}
35
+ {/* href="https://showai.ai"*/}
36
+ {/* target="_blank"*/}
37
+ {/* >*/}
38
+ {/* ShowAI*/}
39
+ {/* </Link>*/}
40
+ {/* .*/}
41
+ </p>
42
+ </div>
43
+ </motion.div>
44
+ );
45
+ };
46
+
47
+ export default Overview;
@@ -0,0 +1,41 @@
1
+ import { IFileUpload } from '../../types/flowise.type';
2
+ import { LoaderIcon } from './Icons';
3
+
4
+ export const PreviewAttachment = ({
5
+ attachment,
6
+ isUploading = false,
7
+ }: {
8
+ attachment: IFileUpload;
9
+ isUploading?: boolean;
10
+ }) => {
11
+ const { name, data, mime, tempId } = attachment;
12
+ return (
13
+ <div className="flex flex-col gap-2">
14
+ <div className="w-30 p-2 max-w-[200px] bg-muted rounded-md relative flex flex-col items-center justify-center">
15
+ {data ? (
16
+ mime.startsWith('image') ? (
17
+ <img
18
+ key={tempId}
19
+ src={data}
20
+ alt={name ?? 'An image attachment'}
21
+ className="rounded-md size-full object-contain"
22
+ />
23
+ ) : (
24
+ <div className="w-full text-left truncate">{name}</div>
25
+ )
26
+ ) : (
27
+ <div className="w-full text-left truncate">{name}</div>
28
+ )}
29
+
30
+ {isUploading && (
31
+ <div className="animate-spin absolute text-zinc-500">
32
+ <LoaderIcon />
33
+ </div>
34
+ )}
35
+ </div>
36
+ <div className="text-xs text-zinc-500 max-w-[200px] truncate ">
37
+ {name}
38
+ </div>
39
+ </div>
40
+ );
41
+ };
@@ -0,0 +1,55 @@
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 };
@@ -0,0 +1,23 @@
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 };
@@ -0,0 +1 @@
1
+ export const agentWidgetElementName = 'clik-agent-widget';
package/src/env.d.ts ADDED
@@ -0,0 +1,10 @@
1
+ // export {};
2
+ //
3
+ // declare module 'solid-js' {
4
+ // namespace JSX {
5
+ // interface CustomEvents {
6
+ // click: MouseEvent;
7
+ // pointerdown: PointerEvent;
8
+ // }
9
+ // }
10
+ // }
@@ -0,0 +1,47 @@
1
+ import { FC } from 'react';
2
+ import { ConfigurationProvider } from '../../hooks/useConfiguration';
3
+ import { EVENT_TYPE } from '../../models';
4
+ import Agent from '../../components/Agent';
5
+ import { ChatDataProvider } from '../../hooks/useChatData';
6
+ import styles from '../../assets/tailwindcss.css';
7
+ import commonStyles from '../../assets/common.css';
8
+
9
+ export type AgentWidgetType = {
10
+ apiHost: string;
11
+ agentId: string;
12
+ overrideConfig?: {
13
+ chatId?: string | undefined;
14
+ } & Record<string, unknown>;
15
+ theme?: {
16
+ avatar?: string;
17
+ } & Record<string, unknown>;
18
+ listeners?: Record<EVENT_TYPE, (props: any) => void>;
19
+ };
20
+ const AgentWidget: FC<AgentWidgetType> = (props: AgentWidgetType) => {
21
+ return (
22
+ <div className="w-full h-full text-sm">
23
+ <style>{styles}</style>
24
+ <style>{commonStyles}</style>
25
+ <ConfigurationProvider
26
+ config={{
27
+ apiHost: props.apiHost,
28
+ agentId: props.agentId,
29
+ listeners: props.listeners,
30
+ overrideConfig: props.overrideConfig,
31
+ theme: props.theme,
32
+ }}
33
+ >
34
+ <ChatDataProvider
35
+ data={{
36
+ chatId: props.overrideConfig?.chatId,
37
+ theme: props.theme,
38
+ }}
39
+ >
40
+ <Agent />
41
+ </ChatDataProvider>
42
+ </ConfigurationProvider>
43
+ </div>
44
+ );
45
+ };
46
+
47
+ export default AgentWidget;
@@ -0,0 +1 @@
1
+ declare module '*.css';