@@ -29,10 +29,12 @@ export function Terminal({ scriptPath, onClose, mode = 'local', server, isUpdate
2929 const [ lastInputSent , setLastInputSent ] = useState < string | null > ( null ) ;
3030 const [ isMobile , setIsMobile ] = useState ( false ) ;
3131 const [ isStopped , setIsStopped ] = useState ( false ) ;
32+ const [ isTerminalReady , setIsTerminalReady ] = useState ( false ) ;
3233 const terminalRef = useRef < HTMLDivElement > ( null ) ;
3334 const xtermRef = useRef < any > ( null ) ;
3435 const fitAddonRef = useRef < any > ( null ) ;
3536 const wsRef = useRef < WebSocket | null > ( null ) ;
37+ const inputHandlerRef = useRef < ( ( data : string ) => void ) | null > ( null ) ;
3638 const [ executionId , setExecutionId ] = useState ( ( ) => `exec_${ Date . now ( ) } _${ Math . random ( ) . toString ( 36 ) . substr ( 2 , 9 ) } ` ) ;
3739 const isConnectingRef = useRef < boolean > ( false ) ;
3840 const hasConnectedRef = useRef < boolean > ( false ) ;
@@ -180,6 +182,20 @@ export function Terminal({ scriptPath, onClose, mode = 'local', server, isUpdate
180182 terminal . refresh ( 0 , terminal . rows - 1 ) ;
181183 // Ensure cursor is properly positioned
182184 terminal . focus ( ) ;
185+
186+ // Force focus on the terminal element
187+ terminalElement . focus ( ) ;
188+ terminalElement . click ( ) ;
189+
190+ // Add click handler to ensure terminal stays focused
191+ const focusHandler = ( ) => {
192+ terminal . focus ( ) ;
193+ terminalElement . focus ( ) ;
194+ } ;
195+ terminalElement . addEventListener ( 'click' , focusHandler ) ;
196+
197+ // Store the handler for cleanup
198+ ( terminalElement as any ) . focusHandler = focusHandler ;
183199 } , 100 ) ;
184200
185201 // Fit after a small delay to ensure proper sizing
@@ -213,18 +229,10 @@ export function Terminal({ scriptPath, onClose, mode = 'local', server, isUpdate
213229 // Store references
214230 xtermRef . current = terminal ;
215231 fitAddonRef . current = fitAddon ;
232+
233+ // Mark terminal as ready
234+ setIsTerminalReady ( true ) ;
216235
217- // Handle terminal input
218- terminal . onData ( ( data ) => {
219- if ( wsRef . current && wsRef . current . readyState === WebSocket . OPEN ) {
220- const message = {
221- action : 'input' ,
222- executionId,
223- input : data
224- } ;
225- wsRef . current . send ( JSON . stringify ( message ) ) ;
226- }
227- } ) ;
228236
229237 return ( ) => {
230238 terminal . dispose ( ) ;
@@ -236,18 +244,51 @@ export function Terminal({ scriptPath, onClose, mode = 'local', server, isUpdate
236244 void initTerminal ( ) ;
237245 } , 50 ) ;
238246
239- return ( ) => {
240- clearTimeout ( timeoutId ) ;
241- if ( terminalElement && ( terminalElement as any ) . resizeHandler ) {
242- window . removeEventListener ( 'resize' , ( terminalElement as any ) . resizeHandler as ( this : Window , ev : UIEvent ) => any ) ;
243- }
244- if ( xtermRef . current ) {
245- xtermRef . current . dispose ( ) ;
246- xtermRef . current = null ;
247- fitAddonRef . current = null ;
247+ return ( ) => {
248+ clearTimeout ( timeoutId ) ;
249+ if ( terminalElement && ( terminalElement as any ) . resizeHandler ) {
250+ window . removeEventListener ( 'resize' , ( terminalElement as any ) . resizeHandler as ( this : Window , ev : UIEvent ) => any ) ;
251+ }
252+ if ( terminalElement && ( terminalElement as any ) . focusHandler ) {
253+ terminalElement . removeEventListener ( 'click' , ( terminalElement as any ) . focusHandler as ( this : HTMLDivElement , ev : PointerEvent ) => any ) ;
254+ }
255+ if ( xtermRef . current ) {
256+ xtermRef . current . dispose ( ) ;
257+ xtermRef . current = null ;
258+ fitAddonRef . current = null ;
259+ setIsTerminalReady ( false ) ;
260+ }
261+ } ;
262+ } , [ isClient , isMobile ] ) ;
263+
264+ // Handle terminal input with current executionId
265+ useEffect ( ( ) => {
266+ if ( ! isTerminalReady || ! xtermRef . current ) {
267+ return ;
268+ }
269+
270+ const terminal = xtermRef . current ;
271+
272+ const handleData = ( data : string ) => {
273+ if ( wsRef . current && wsRef . current . readyState === WebSocket . OPEN ) {
274+ const message = {
275+ action : 'input' ,
276+ executionId,
277+ input : data
278+ } ;
279+ wsRef . current . send ( JSON . stringify ( message ) ) ;
248280 }
249281 } ;
250- } , [ isClient , isMobile ] ) ; // eslint-disable-line react-hooks/exhaustive-deps
282+
283+ // Store the handler reference
284+ inputHandlerRef . current = handleData ;
285+ terminal . onData ( handleData ) ;
286+
287+ return ( ) => {
288+ // Clear the handler reference
289+ inputHandlerRef . current = null ;
290+ } ;
291+ } , [ executionId , isTerminalReady ] ) ; // Depend on terminal ready state
251292
252293 useEffect ( ( ) => {
253294 // Prevent multiple connections in React Strict Mode
0 commit comments