@@ -3,8 +3,15 @@ import RMarkdown from "react-markdown";
3
3
import remarkGfm from "remark-gfm" ;
4
4
import { Prism as SyntaxHighlighter } from "react-syntax-highlighter" ;
5
5
import { a11yDark } from "react-syntax-highlighter/dist/esm/styles/prism" ;
6
- import { useCallback , useEffect , useRef , useState } from "react" ;
7
- import { ArrowDownCircleFill } from "react-bootstrap-icons" ;
6
+ import {
7
+ FormEvent ,
8
+ KeyboardEvent ,
9
+ useCallback ,
10
+ useEffect ,
11
+ useRef ,
12
+ useState ,
13
+ } from "react" ;
14
+ import { ArrowDownCircleFill , ArrowUpSquareFill } from "react-bootstrap-icons" ;
8
15
9
16
function useLocal < T extends object | undefined > ( input : T ) {
10
17
return useRef ( proxy ( input ) ) . current as T ;
@@ -125,17 +132,23 @@ const History = ({ history }: { history: any[] }) => {
125
132
switch ( item . type ) {
126
133
case "system" :
127
134
return (
128
- < div key = { index } className = "border-b-2 py-4 text-lg font-bold" >
129
- < div className = "text-l sticky top-0 bg-white" > Assistant:</ div >
135
+ < div key = { index } className = "pt-4 text-lg font-bold" >
136
+ < div className = "sticky top-0 flex items-center gap-2 bg-white" >
137
+ < div className = "text-l text-primary" > Assistant</ div >
138
+ < div className = "flex-1 border-b-2" />
139
+ </ div >
130
140
< div className = "prose p-2" >
131
141
< Mardown > { item . message } </ Mardown >
132
142
</ div >
133
143
</ div >
134
144
) ;
135
145
case "user" :
136
146
return (
137
- < div key = { index } className = "border-b-2 py-4 text-lg font-bold" >
138
- < div className = "sticky top-0 bg-white" > You:</ div >
147
+ < div key = { index } className = "pt-4 text-lg font-bold" >
148
+ < div className = "sticky top-0 flex items-center gap-2 bg-white" >
149
+ < div className = "text-l text-secondary" > You</ div >
150
+ < div className = "flex-1 border-b-2" />
151
+ </ div >
139
152
< div className = "prose p-2" >
140
153
< Mardown > { item . message } </ Mardown >
141
154
</ div >
@@ -157,14 +170,61 @@ const History = ({ history }: { history: any[] }) => {
157
170
) ;
158
171
} ;
159
172
160
- const Input = ( ) => {
173
+ interface InputProps {
174
+ onSubmit : ( message : string ) => void ;
175
+ }
176
+
177
+ const Input = ( { onSubmit } : InputProps ) => {
178
+ const ref = useRef < HTMLTextAreaElement > ( null ) ;
179
+ const [ message , setMessage ] = useState ( "" ) ;
180
+ const isEmpty = message . trim ( ) . length === 0 ;
181
+
182
+ const forwardSubmit = (
183
+ e : FormEvent < HTMLFormElement > | KeyboardEvent < HTMLTextAreaElement > ,
184
+ ) => {
185
+ e . preventDefault ( ) ;
186
+ onSubmit ( message ) ;
187
+ setMessage ( "" ) ;
188
+ } ;
189
+
190
+ const handleKeyDown = ( e : React . KeyboardEvent < HTMLTextAreaElement > ) => {
191
+ if ( e . key === "Enter" && ( e . ctrlKey || e . metaKey ) ) {
192
+ forwardSubmit ( e ) ;
193
+ }
194
+ } ;
195
+
196
+ useEffect ( ( ) => {
197
+ if ( ref . current ) {
198
+ // Find a way to make this cleaner, up to 5 lines
199
+ if ( ref . current . scrollHeight / 32 < 5 ) {
200
+ ref . current . style . height = "auto" ;
201
+ ref . current . style . height = ref . current . scrollHeight + "px" ;
202
+ }
203
+ }
204
+ } , [ message ] ) ;
205
+
161
206
return (
162
- < form >
207
+ < form onSubmit = { forwardSubmit } className = "relative" >
208
+ < button
209
+ type = "submit"
210
+ className = "absolute bottom-4 right-2 opacity-60 hover:opacity-100 disabled:hidden"
211
+ disabled = { isEmpty }
212
+ >
213
+ < ArrowUpSquareFill size = "2rem" />
214
+ </ button >
215
+
163
216
< textarea
164
- className = "textarea textarea-primary w-full rounded-none pl-5"
165
- placeholder = "Bio"
217
+ name = "message"
218
+ ref = { ref }
219
+ autoFocus
220
+ value = { message }
221
+ onChange = { ( e ) => setMessage ( e . currentTarget . value ) }
222
+ className = "textarea textarea-primary w-full rounded-none bg-white pr-14"
223
+ placeholder = "Message your assistant..."
224
+ onKeyDown = { handleKeyDown }
225
+ rows = { 1 }
226
+ style = { { resize : "none" } }
166
227
> </ textarea >
167
- < span className = "button" > </ span >
168
228
</ form >
169
229
) ;
170
230
} ;
@@ -178,7 +238,11 @@ export const Chat = () => {
178
238
return (
179
239
< div className = "flex h-full max-h-full flex-col gap-3 bg-white" >
180
240
< History history = { historySnap } />
181
- < Input />
241
+ < Input
242
+ onSubmit = { ( data ) => {
243
+ console . warn ( data ) ;
244
+ } }
245
+ />
182
246
</ div >
183
247
) ;
184
248
} ;
0 commit comments