1- ' use client' ;
1+ " use client" ;
22
3- import { useState , useEffect , useRef } from ' react' ;
4- import MessageList from ' ./MessageList' ;
5- import MessageInput from ' ./MessageInput' ;
6- import { useSearchParams } from ' next/navigation'
3+ import { useState , useEffect , useRef } from " react" ;
4+ import MessageList from " ./MessageList" ;
5+ import MessageInput from " ./MessageInput" ;
6+ import { useSearchParams } from " next/navigation" ;
77
88interface Message {
99 role : string ;
@@ -25,10 +25,10 @@ interface StatusChangeEvent {
2525export default function ChatInterface ( ) {
2626 const [ messages , setMessages ] = useState < Message [ ] > ( [ ] ) ;
2727 const [ loading , setLoading ] = useState < boolean > ( false ) ;
28- const [ serverStatus , setServerStatus ] = useState < string > ( ' unknown' ) ;
28+ const [ serverStatus , setServerStatus ] = useState < string > ( " unknown" ) ;
2929 const searchParams = useSearchParams ( ) ;
3030 // null port gets converted to NaN
31- const parsedPort = parseInt ( searchParams . get ( ' port' ) as string ) ;
31+ const parsedPort = parseInt ( searchParams . get ( " port" ) as string ) ;
3232 const port = isNaN ( parsedPort ) ? 3284 : parsedPort ;
3333 const AgentAPIUrl = `http://localhost:${ port } ` ;
3434 const eventSourceRef = useRef < EventSource | null > ( null ) ;
@@ -40,159 +40,177 @@ export default function ChatInterface() {
4040 if ( eventSourceRef . current ) {
4141 eventSourceRef . current . close ( ) ;
4242 }
43-
43+
4444 const eventSource = new EventSource ( `${ AgentAPIUrl } /events` ) ;
4545 eventSourceRef . current = eventSource ;
46-
46+
4747 // Handle message updates
48- eventSource . addEventListener ( ' message_update' , ( event ) => {
48+ eventSource . addEventListener ( " message_update" , ( event ) => {
4949 const data : MessageUpdateEvent = JSON . parse ( event . data ) ;
50-
51- setMessages ( prevMessages => {
50+
51+ setMessages ( ( prevMessages ) => {
5252 // Check if message with this ID already exists
53- const existingIndex = prevMessages . findIndex ( m => m . id === data . id ) ;
54-
53+ const existingIndex = prevMessages . findIndex ( ( m ) => m . id === data . id ) ;
54+
5555 if ( existingIndex !== - 1 ) {
5656 // Update existing message
5757 const updatedMessages = [ ...prevMessages ] ;
5858 updatedMessages [ existingIndex ] = {
5959 role : data . role ,
6060 content : data . message ,
61- id : data . id
61+ id : data . id ,
6262 } ;
6363 return updatedMessages ;
6464 } else {
6565 // Add new message
66- return [ ...prevMessages , {
67- role : data . role ,
68- content : data . message ,
69- id : data . id
70- } ] ;
66+ return [
67+ ...prevMessages ,
68+ {
69+ role : data . role ,
70+ content : data . message ,
71+ id : data . id ,
72+ } ,
73+ ] ;
7174 }
7275 } ) ;
7376 } ) ;
74-
77+
7578 // Handle status changes
76- eventSource . addEventListener ( ' status_change' , ( event ) => {
79+ eventSource . addEventListener ( " status_change" , ( event ) => {
7780 const data : StatusChangeEvent = JSON . parse ( event . data ) ;
7881 setServerStatus ( data . status ) ;
7982 } ) ;
80-
83+
8184 // Handle connection open (server is online)
8285 eventSource . onopen = ( ) => {
8386 // Connection is established, but we'll wait for status_change event
8487 // for the actual server status
8588 } ;
86-
89+
8790 // Handle connection errors
8891 eventSource . onerror = ( error ) => {
89- console . error ( ' EventSource error:' , error ) ;
90- setServerStatus ( ' offline' ) ;
91-
92+ console . error ( " EventSource error:" , error ) ;
93+ setServerStatus ( " offline" ) ;
94+
9295 // Try to reconnect after delay
9396 setTimeout ( ( ) => {
9497 if ( eventSourceRef . current ) {
9598 setupEventSource ( ) ;
9699 }
97100 } , 3000 ) ;
98101 } ;
99-
102+
100103 return eventSource ;
101104 } ;
102-
105+
103106 // Initial setup
104107 const eventSource = setupEventSource ( ) ;
105-
108+
106109 // Clean up on component unmount
107110 return ( ) => {
108111 eventSource . close ( ) ;
109112 } ;
110113 } , [ AgentAPIUrl ] ) ;
111-
114+
112115 const [ error , setError ] = useState < string | null > ( null ) ;
113116
114117 // Send a new message
115- const sendMessage = async ( content : string , type : 'user' | 'raw' = 'user' ) => {
118+ const sendMessage = async (
119+ content : string ,
120+ type : "user" | "raw" = "user"
121+ ) => {
116122 // For user messages, require non-empty content
117- if ( type === ' user' && ! content . trim ( ) ) return ;
118-
123+ if ( type === " user" && ! content . trim ( ) ) return ;
124+
119125 // Clear any previous errors
120126 setError ( null ) ;
121-
127+
122128 // For raw messages, don't set loading state as it's usually fast
123- if ( type === ' user' ) {
129+ if ( type === " user" ) {
124130 setLoading ( true ) ;
125131 }
126-
132+
127133 try {
128134 const response = await fetch ( `${ AgentAPIUrl } /message` , {
129- method : ' POST' ,
135+ method : " POST" ,
130136 headers : {
131- ' Content-Type' : ' application/json' ,
137+ " Content-Type" : " application/json" ,
132138 } ,
133- body : JSON . stringify ( {
134- content : content ,
135- type
139+ body : JSON . stringify ( {
140+ content : content ,
141+ type,
136142 } ) ,
137143 } ) ;
138-
144+
139145 if ( ! response . ok ) {
140146 const errorData = await response . json ( ) ;
141- console . error ( ' Failed to send message:' , errorData ) ;
147+ console . error ( " Failed to send message:" , errorData ) ;
142148 const detail = errorData . detail ;
143- // eslint-disable-next-line @typescript-eslint/no-explicit-any
144- const messages = "errors" in errorData ? errorData . errors . map ( ( e : any ) => e . message ) . join ( ", " ) : "" ;
149+ const messages =
150+ "errors" in errorData
151+ ? // eslint-disable-next-line @typescript-eslint/no-explicit-any
152+ errorData . errors . map ( ( e : any ) => e . message ) . join ( ", " )
153+ : "" ;
145154
146155 const fullDetail = `${ detail } : ${ messages } ` ;
147156 setError ( `Failed to send message: ${ fullDetail } ` ) ;
148157 // Auto-clear error after 5 seconds
149158 setTimeout ( ( ) => setError ( null ) , 5000 ) ;
150159 }
151- // eslint-disable-next-line @typescript-eslint/no-explicit-any
160+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
152161 } catch ( error : any ) {
153- console . error ( ' Error sending message:' , error ) ;
162+ console . error ( " Error sending message:" , error ) ;
154163 const detail = error . detail ;
155- // eslint-disable-next-line @typescript-eslint/no-explicit-any
156- const messages = "errors" in error ? error . errors . map ( ( e : any ) => e . message ) . join ( "\n" ) : "" ;
164+ const messages =
165+ "errors" in error
166+ ? // eslint-disable-next-line @typescript-eslint/no-explicit-any
167+ error . errors . map ( ( e : any ) => e . message ) . join ( "\n" )
168+ : "" ;
157169
158170 const fullDetail = `${ detail } : ${ messages } ` ;
159171
160172 setError ( `Error sending message: ${ fullDetail } ` ) ;
161173 // Auto-clear error after 5 seconds
162174 setTimeout ( ( ) => setError ( null ) , 5000 ) ;
163175 } finally {
164- if ( type === ' user' ) {
176+ if ( type === " user" ) {
165177 setLoading ( false ) ;
166178 }
167179 }
168180 } ;
169-
181+
170182 return (
171183 < div className = "flex flex-col h-[80vh] bg-gray-100 rounded-lg overflow-hidden border border-gray-300 shadow-lg w-full max-w-[95vw]" >
172184 < div className = "p-3 bg-gray-800 text-white text-sm flex justify-between items-center" >
173185 < span > AgentAPI Chat</ span >
174186 < span className = "flex items-center" >
175- < span className = { `w-2 h-2 rounded-full mr-2 ${ [ "offline" , "unknown" ] . includes ( serverStatus ) ? 'bg-red-500' : 'bg-green-500' } ` } > </ span >
187+ < span
188+ className = { `w-2 h-2 rounded-full mr-2 ${
189+ [ "offline" , "unknown" ] . includes ( serverStatus )
190+ ? "bg-red-500"
191+ : "bg-green-500"
192+ } `}
193+ > </ span >
176194 < span > Status: { serverStatus } </ span >
177195 < span className = "ml-2" > Port: { port } </ span >
178196 </ span >
179197 </ div >
180-
198+
181199 { error && (
182200 < div className = "bg-red-100 border border-red-400 text-red-700 px-4 py-2 text-sm relative" >
183201 < span className = "block sm:inline" > { error } </ span >
184- < button
202+ < button
185203 onClick = { ( ) => setError ( null ) }
186204 className = "absolute top-0 bottom-0 right-0 px-4 py-2"
187205 >
188206 ×
189207 </ button >
190208 </ div >
191209 ) }
192-
210+
193211 < MessageList messages = { messages } />
194-
212+
195213 < MessageInput onSendMessage = { sendMessage } disabled = { loading } />
196214 </ div >
197215 ) ;
198- }
216+ }
0 commit comments