5
5
ArrowUpIcon ,
6
6
Loader2Icon ,
7
7
MicIcon ,
8
+ PaperclipIcon ,
8
9
PauseIcon ,
10
+ UploadIcon ,
9
11
XIcon ,
10
12
} from "lucide-react" ;
11
13
import { useEffect , useRef , useState } from "react" ;
@@ -41,6 +43,7 @@ export type Props = {
41
43
onStopRecord : ( ) => void ;
42
44
attachments : Attachment [ ] ;
43
45
onRemoveAttachment : ( attachment : Attachment ) => void ;
46
+ onAddAttachment : ( newAttachments : Attachment [ ] ) => void ;
44
47
} ;
45
48
46
49
export const ChatInput = ( {
@@ -53,12 +56,44 @@ export const ChatInput = ({
53
56
onStopRecord,
54
57
attachments,
55
58
onRemoveAttachment,
59
+ onAddAttachment,
56
60
} : Props ) => {
57
61
const inputRef = useRef < HTMLTextAreaElement > ( null ) ;
58
62
const { onKeyDown } = useEnterSubmit ( {
59
63
onSubmit,
60
64
} ) ;
61
65
const [ model , setModel ] = useState < Models > ( getSettings ( ) . model ) ;
66
+ const fileInputRef = useRef < HTMLInputElement > ( null ) ;
67
+
68
+ const handleFileUpload = ( ) => {
69
+ fileInputRef . current ?. click ( ) ;
70
+ } ;
71
+
72
+ const convertToBase64 = ( file : File ) : Promise < string > => {
73
+ return new Promise ( ( resolve , reject ) => {
74
+ const reader = new FileReader ( ) ;
75
+ reader . readAsDataURL ( file ) ;
76
+ reader . onload = ( ) => resolve ( reader . result as string ) ;
77
+ reader . onerror = ( error ) => reject ( error ) ;
78
+ } ) ;
79
+ } ;
80
+
81
+ const handleFileChange = async ( e : React . ChangeEvent < HTMLInputElement > ) => {
82
+ if ( e . target . files ) {
83
+ const filesArray = Array . from ( e . target . files ) ;
84
+ const attachmentsPromises = filesArray . map ( async ( file ) => {
85
+ const base64 = await convertToBase64 ( file ) ;
86
+ return {
87
+ url : base64 ,
88
+ name : file . name ,
89
+ contentType : file . type ,
90
+ } as Attachment ;
91
+ } ) ;
92
+
93
+ const newAttachments = await Promise . all ( attachmentsPromises ) ;
94
+ onAddAttachment ( newAttachments ) ;
95
+ }
96
+ } ;
62
97
63
98
useEffect ( ( ) => {
64
99
if ( inputRef . current ) {
@@ -107,6 +142,23 @@ export const ChatInput = ({
107
142
onChange = { ( e ) => setInput ( e . target . value ) }
108
143
/>
109
144
145
+ < input
146
+ type = "file"
147
+ accept = "image/*"
148
+ multiple
149
+ ref = { fileInputRef }
150
+ style = { { display : "none" } }
151
+ onChange = { handleFileChange }
152
+ />
153
+ < Button
154
+ variant = "outline"
155
+ size = "icon"
156
+ className = "w-8 h-8 bg-transparent"
157
+ onClick = { handleFileUpload }
158
+ >
159
+ < PaperclipIcon className = "w-4 h-4" />
160
+ </ Button >
161
+
110
162
< TooltipProvider >
111
163
< Tooltip >
112
164
< TooltipTrigger asChild >
@@ -116,7 +168,8 @@ export const ChatInput = ({
116
168
recording ? onStopRecord ( ) : onStartRecord ( ) ;
117
169
} }
118
170
size = "icon"
119
- className = "w-8 h-8 disabled:pointer-events-auto"
171
+ variant = "outline"
172
+ className = "w-8 h-8 bg-transparent disabled:pointer-events-auto"
120
173
>
121
174
{ recording ? (
122
175
< PauseIcon className = "w-4 h-4" />
0 commit comments