@@ -29,10 +29,20 @@ export default function ChatInterface() {
2929 const searchParams = useSearchParams ( ) ;
3030 // null port gets converted to NaN
3131 const parsedPort = parseInt ( searchParams . get ( "port" ) as string ) ;
32- const port = isNaN ( parsedPort ) ? 3284 : parsedPort ;
32+ // We're setting port via URL query param, not directly with setPort
33+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
34+ const [ port , setPort ] = useState < number > (
35+ isNaN ( parsedPort ) ? 3284 : parsedPort
36+ ) ;
37+ const [ portInput , setPortInput ] = useState < string > ( port . toString ( ) ) ;
3338 const AgentAPIUrl = `http://localhost:${ port } ` ;
3439 const eventSourceRef = useRef < EventSource | null > ( null ) ;
3540
41+ // Update portInput when port changes
42+ useEffect ( ( ) => {
43+ setPortInput ( port . toString ( ) ) ;
44+ } , [ port ] ) ;
45+
3646 // Set up SSE connection to the events endpoint
3747 useEffect ( ( ) => {
3848 // Function to create and set up EventSource
@@ -179,23 +189,82 @@ export default function ChatInterface() {
179189 }
180190 } ;
181191
192+ const updatePort = ( ) => {
193+ const newPort = parseInt ( portInput ) ;
194+ if ( ! isNaN ( newPort ) && newPort > 0 && newPort < 65536 ) {
195+ window . location . href = `?port=${ newPort } ` ;
196+ } else {
197+ setError ( "Invalid port number. Please enter a number between 1-65535." ) ;
198+ setTimeout ( ( ) => setError ( null ) , 5000 ) ;
199+ }
200+ } ;
201+
182202 return (
183203 < 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]" >
184- < div className = "p-3 bg-gray-800 text-white text-sm flex justify-between items-center" >
204+ < div className = "p-3 bg-gray-800 text-white text-sm flex items-center justify-between " >
185205 < span > AgentAPI Chat</ span >
186- < span className = "flex items-center" >
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 >
194- < span > Status: { serverStatus } </ span >
195- < span className = "ml-2" > Port: { port } </ span >
196- </ span >
206+ < div className = "flex items-center space-x-3" >
207+ < div className = "flex items-center" >
208+ < label htmlFor = "port-input" className = "text-white mr-1" >
209+ Port:
210+ </ label >
211+ < input
212+ id = "port-input"
213+ type = "text"
214+ value = { portInput }
215+ onChange = { ( e ) => setPortInput ( e . target . value ) }
216+ className = "w-16 px-1 py-0.5 text-xs rounded border border-gray-400 bg-gray-700 text-white"
217+ onKeyDown = { ( e ) => e . key === "Enter" && updatePort ( ) }
218+ />
219+ < button
220+ onClick = { updatePort }
221+ className = "ml-1 px-2 py-0.5 text-xs bg-gray-600 hover:bg-gray-500 rounded"
222+ >
223+ Apply
224+ </ button >
225+ </ div >
226+ < div className = "flex items-center" >
227+ < span
228+ className = { `w-2 h-2 rounded-full mr-2 ${
229+ [ "offline" , "unknown" ] . includes ( serverStatus )
230+ ? "bg-red-500"
231+ : "bg-green-500"
232+ } `}
233+ > </ span >
234+ < span > Status: { serverStatus } </ span >
235+ </ div >
236+ </ div >
197237 </ div >
198238
239+ { ( serverStatus === "offline" || serverStatus === "unknown" ) && (
240+ < div className = "bg-yellow-100 border-y border-yellow-400 text-yellow-800 px-4 py-3 flex items-center justify-between font-medium" >
241+ < div className = "flex items-center" >
242+ < svg
243+ className = "w-5 h-5 mr-2"
244+ fill = "currentColor"
245+ viewBox = "0 0 20 20"
246+ xmlns = "http://www.w3.org/2000/svg"
247+ >
248+ < path
249+ fillRule = "evenodd"
250+ d = "M8.257 3.099c.765-1.36 2.722-1.36 3.486 0l5.58 9.92c.75 1.334-.213 2.98-1.742 2.98H4.42c-1.53 0-2.493-1.646-1.743-2.98l5.58-9.92zM11 13a1 1 0 11-2 0 1 1 0 012 0zm-1-8a1 1 0 00-1 1v3a1 1 0 002 0V6a1 1 0 00-1-1z"
251+ clipRule = "evenodd"
252+ />
253+ </ svg >
254+ < span >
255+ API server is offline. Please start the API server on localhost:
256+ { port } .
257+ </ span >
258+ </ div >
259+ < button
260+ onClick = { ( ) => window . location . reload ( ) }
261+ className = "bg-yellow-200 px-3 py-1 rounded text-xs hover:bg-yellow-300"
262+ >
263+ Retry Connection
264+ </ button >
265+ </ div >
266+ ) }
267+
199268 { error && (
200269 < div className = "bg-red-100 border border-red-400 text-red-700 px-4 py-2 text-sm relative" >
201270 < span className = "block sm:inline" > { error } </ span >
0 commit comments