From b44be7232e9e6edc8b451a69061bd926918bd1f3 Mon Sep 17 00:00:00 2001 From: Jeremy Wijaya Date: Wed, 1 Oct 2025 13:57:06 +0700 Subject: [PATCH] fix: buffer terminal input pre-session --- components/terminal/TerminalSurface.tsx | 60 ++++++++++++++++++------- 1 file changed, 44 insertions(+), 16 deletions(-) diff --git a/components/terminal/TerminalSurface.tsx b/components/terminal/TerminalSurface.tsx index 25cf44e..19984e7 100644 --- a/components/terminal/TerminalSurface.tsx +++ b/components/terminal/TerminalSurface.tsx @@ -54,6 +54,7 @@ const TerminalSurface = forwardRef( const terminalRef = useRef(null); const fitAddonRef = useRef(null); const activeSessionIdRef = useRef(null); + const pendingInputRef = useRef([]); const resizeObserverRef = useRef(null); const inputListenerRef = useRef(null); const onInputRef = useRef( @@ -77,6 +78,26 @@ const TerminalSurface = forwardRef( | null >(null); + const dispatchInput = useCallback((sessionId: string, chunk: string) => { + if (!chunk) { + return; + } + + const processed = onInputRef.current?.({ sessionId, input: chunk }); + + if (processed === null) { + return; + } + + const outbound = typeof processed === "string" ? processed : chunk; + + if (!outbound) { + return; + } + + sendTerminalInputRef.current?.({ sessionId, input: outbound }); + }, []); + const runFitAndEmit = useCallback(() => { pendingFitFrameRef.current = null; @@ -224,25 +245,17 @@ const TerminalSurface = forwardRef( let dataDisposable: IDisposable | undefined; let binaryDisposable: IDisposable | undefined; - const handleInput = (input: string) => { - const sessionId = activeSessionIdRef.current; - if (!sessionId) { - return; - } - - const processed = onInputRef.current?.({ sessionId, input }); - - if (processed === null) { + const handleInput = (chunk: string) => { + if (!chunk) { return; } - - const outbound = typeof processed === "string" ? processed : input; - - if (!outbound) { + const sessionId = activeSessionIdRef.current; + if (!sessionId) { + pendingInputRef.current.push(chunk); return; } - sendTerminalInputRef.current?.({ sessionId, input: outbound }); + dispatchInput(sessionId, chunk); }; if (containerRef.current) { @@ -292,7 +305,7 @@ const TerminalSurface = forwardRef( fitAddonRef.current = null; resizeObserverRef.current = null; }; - }, [scheduleFitAndEmit]); + }, [scheduleFitAndEmit, dispatchInput]); useEffect(() => { if (!terminalRef.current) { @@ -328,6 +341,12 @@ const TerminalSurface = forwardRef( const handleReady = (payload: { sessionId: string }) => { activeSessionIdRef.current = payload.sessionId; lastKnownGeometryRef.current = null; + if (pendingInputRef.current.length > 0) { + for (const chunk of pendingInputRef.current) { + dispatchInput(payload.sessionId, chunk); + } + pendingInputRef.current = []; + } queueMicrotask(() => { scheduleFitAndEmit(); }); @@ -375,6 +394,7 @@ const TerminalSurface = forwardRef( } activeSessionIdRef.current = null; lastKnownGeometryRef.current = null; + pendingInputRef.current = []; onExit?.(payload); }; @@ -389,7 +409,15 @@ const TerminalSurface = forwardRef( socket.off("terminal:error", handleError); socket.off("terminal:exit", handleExit); }; - }, [socket, onReady, onData, onError, onExit, scheduleFitAndEmit]); + }, [ + socket, + onReady, + onData, + onError, + onExit, + scheduleFitAndEmit, + dispatchInput, + ]); useImperativeHandle( ref,