11import { useState } from "react" ;
22import Markdown from "react-markdown" ;
33import { fetchEventSource } from "@microsoft/fetch-event-source" ;
4+ import { Ring } from "ldrs/react" ;
5+ import "ldrs/react/Ring.css" ;
46
57type Messages = { role : "user" | "assistant" ; content : string } [ ] ;
68type Sources = { title : string ; file_path : string } [ ] ;
79
8- function Messages ( { messages } : { messages : Messages } ) {
10+ function Messages ( {
11+ messages,
12+ loading,
13+ } : {
14+ messages : Messages ;
15+ loading : boolean ;
16+ } ) {
17+ const classes = {
18+ base : "w-fit max-w-3/4 rounded p-4" ,
19+ user : "bg-cl1-brand-orange text-cl1-black self-end" ,
20+ assistant : "self-start bg-(--sl-color-bg-nav)" ,
21+ } ;
22+
923 return (
1024 < div className = "flex flex-col justify-center gap-4" >
1125 { messages
1226 . filter ( ( message ) => Boolean ( message . content ) )
1327 . map ( ( message , index ) => (
1428 < div
1529 key = { index }
16- className = { `w-fit max-w-3/4 rounded p-4 ${ message . role === "user" ? "bg-cl1-brand-orange text-cl1-black self-end" : "self-start bg-(--sl-color-bg-nav)" } ` }
30+ className = { `${ classes . base } ${ message . role === "user" ? classes . user : classes . assistant } ` }
1731 >
1832 < Markdown > { message . content } </ Markdown >
1933 </ div >
2034 ) ) }
35+ { loading && (
36+ < div className = { `${ classes . base } ${ classes . assistant } ` } >
37+ < Ring size = { 16 } speed = { 1 } color = "var(--color-cl1-brand-orange)" />
38+ </ div >
39+ ) }
2140 </ div >
2241 ) ;
2342}
2443
2544export default function SupportAI ( ) {
2645 const [ threadId , setThreadId ] = useState < string | undefined > ( ) ;
2746 const [ question , setQuestion ] = useState < string > ( "" ) ;
47+ const [ loading , setLoading ] = useState < boolean > ( false ) ;
2848
2949 const [ messages , setMessages ] = useState < Messages > ( [ ] ) ;
3050
3151 async function handleSubmit ( ) {
52+ setLoading ( true ) ;
3253 setMessages ( ( messages ) => [
3354 ...messages ,
3455 { role : "user" , content : question } ,
@@ -62,6 +83,7 @@ export default function SupportAI() {
6283 } ,
6384 onerror ( error ) {
6485 if ( error instanceof Error ) {
86+ setLoading ( false ) ;
6587 setMessages ( ( messages ) => [
6688 ...messages ,
6789 {
@@ -104,6 +126,8 @@ export default function SupportAI() {
104126 if ( ! response ) return ;
105127
106128 chunkedAnswer += response ;
129+
130+ setLoading ( false ) ;
107131 setMessages ( ( messages ) => {
108132 const newMessages = [ ...messages ] ;
109133 newMessages [ newMessages . length - 1 ] . content = chunkedAnswer ;
@@ -116,16 +140,17 @@ export default function SupportAI() {
116140
117141 return (
118142 < div >
119- < Messages messages = { messages } />
143+ < Messages messages = { messages } loading = { loading } />
120144 < div className = "flex items-center justify-center gap-4" >
121145 < input
122146 className = "w-full rounded p-2"
123147 type = "text"
124148 placeholder = "Ask a question..."
125149 value = { question }
150+ disabled = { loading }
126151 onChange = { ( e ) => setQuestion ( e . target . value ) }
127152 onKeyDown = { async ( e ) => {
128- if ( e . key === "Enter" ) {
153+ if ( e . key === "Enter" && ! loading ) {
129154 e . preventDefault ( ) ;
130155 await handleSubmit ( ) ;
131156 }
@@ -140,12 +165,6 @@ export default function SupportAI() {
140165 not been verified by Cloudflare for accuracy and does not represent
141166 Cloudflare's views.
142167 </ p >
143- < div >
144- < strong > Debug:</ strong >
145- < pre className = "whitespace-pre-wrap" >
146- { JSON . stringify ( { threadId, messages, question } , null , 2 ) }
147- </ pre >
148- </ div >
149168 </ div >
150169 ) ;
151170}
0 commit comments