@@ -25,9 +25,11 @@ export function TaskTerminal({ taskId, taskName, cwd, className, aiMode, descrip
2525 const termRef = useRef < XTerm | null > ( null )
2626 const fitAddonRef = useRef < FitAddon | null > ( null )
2727 const createdTerminalRef = useRef ( false )
28+ const weCreatedTerminalRef = useRef ( false )
2829 const attachedRef = useRef ( false )
2930 const [ terminalId , setTerminalId ] = useState < string | null > ( null )
3031 const [ xtermReady , setXtermReady ] = useState ( false )
32+ const [ xtermOpened , setXtermOpened ] = useState ( false )
3133
3234 const { setTerminalFocused } = useKeyboardContext ( )
3335
@@ -100,7 +102,12 @@ export function TaskTerminal({ taskId, taskName, cwd, className, aiMode, descrip
100102 termRef . current = term
101103 fitAddonRef . current = fitAddon
102104
103- // Initial fit after container is sized
105+ // Mark xterm as opened synchronously - this gates terminal creation
106+ // We can get cols/rows immediately after open(), no need to wait for rAF
107+ setXtermOpened ( true )
108+
109+ // Initial fit after container is sized - this is for visual display only
110+ // WebKit can delay rAF during navigation, so we don't gate creation on this
104111 requestAnimationFrame ( ( ) => {
105112 fitAddon . fit ( )
106113 setXtermReady ( true )
@@ -132,6 +139,7 @@ export function TaskTerminal({ taskId, taskName, cwd, className, aiMode, descrip
132139 term . dispose ( )
133140 termRef . current = null
134141 fitAddonRef . current = null
142+ setXtermOpened ( false )
135143 setXtermReady ( false )
136144 }
137145 } , [ setTerminalFocused ] )
@@ -200,8 +208,9 @@ export function TaskTerminal({ taskId, taskName, cwd, className, aiMode, descrip
200208
201209 // Find existing terminal or create new one
202210 // Wait for terminalsLoaded to ensure we have accurate knowledge of existing terminals
211+ // Use xtermOpened (not xtermReady) to avoid WebKit rAF timing issues during navigation
203212 useEffect ( ( ) => {
204- if ( ! connected || ! cwd || ! xtermReady || ! terminalsLoaded ) return
213+ if ( ! connected || ! cwd || ! xtermOpened || ! terminalsLoaded ) return
205214
206215 // Look for an existing terminal with matching cwd
207216 const existingTerminal = terminals . find ( ( t ) => t . cwd === cwd )
@@ -213,6 +222,7 @@ export function TaskTerminal({ taskId, taskName, cwd, className, aiMode, descrip
213222 // Create terminal only once
214223 if ( ! createdTerminalRef . current && termRef . current ) {
215224 createdTerminalRef . current = true
225+ weCreatedTerminalRef . current = true // Track that THIS component created the terminal
216226 const { cols, rows } = termRef . current
217227 createTerminal ( {
218228 name : taskName ,
@@ -221,7 +231,7 @@ export function TaskTerminal({ taskId, taskName, cwd, className, aiMode, descrip
221231 cwd,
222232 } )
223233 }
224- } , [ connected , cwd , xtermReady , terminalsLoaded , terminals , taskName , createTerminal ] )
234+ } , [ connected , cwd , xtermOpened , terminalsLoaded , terminals , taskName , createTerminal ] )
225235
226236 // Update terminalId when terminal appears in list
227237
@@ -238,11 +248,14 @@ export function TaskTerminal({ taskId, taskName, cwd, className, aiMode, descrip
238248 useEffect ( ( ) => {
239249 if ( ! terminalId || ! termRef . current || ! containerRef . current || attachedRef . current ) return
240250
241- const isNewTerminal = newTerminalIds . has ( terminalId )
242- // Remove from set immediately so startup commands don't run again on re-mount
243- if ( isNewTerminal ) {
244- newTerminalIds . delete ( terminalId )
245- }
251+ // Use our local ref to determine if we created this terminal
252+ // This is more reliable than newTerminalIds which is per-hook-instance
253+ // and can get out of sync when multiple useTerminalWS() hooks exist
254+ const isNewTerminal = weCreatedTerminalRef . current
255+ // Reset immediately so startup commands don't run again on re-mount
256+ weCreatedTerminalRef . current = false
257+ // Also clean up the hook's newTerminalIds for consistency
258+ newTerminalIds . delete ( terminalId )
246259
247260 // Capture current values for use in callbacks
248261 const currentTerminalId = terminalId
0 commit comments