diff --git a/src/textual_webterm/session_manager.py b/src/textual_webterm/session_manager.py index 0f243d6..ad1fe78 100644 --- a/src/textual_webterm/session_manager.py +++ b/src/textual_webterm/session_manager.py @@ -141,12 +141,15 @@ class SessionManager: ) log.info("Created app session %s", session_id) - self.sessions[session_id] = session_process - self.routes[route_key] = session_id - + # Open the session BEFORE registering it, so it's fully initialized + # when other code can access it via sessions/routes dicts await session_process.open(*size) log.debug("Session %s opened and ready", session_id) + # Now register the fully initialized session + self.sessions[session_id] = session_process + self.routes[route_key] = session_id + return session_process async def close_session(self, session_id: SessionID) -> None: diff --git a/src/textual_webterm/terminal_session.py b/src/textual_webterm/terminal_session.py index 8795670..46aac48 100644 --- a/src/textual_webterm/terminal_session.py +++ b/src/textual_webterm/terminal_session.py @@ -64,9 +64,10 @@ class TerminalSession(Session): async def open(self, width: int = 80, height: int = 24) -> None: log.info("Opening terminal session %s with command: %s", self.session_id, self.command) - # Initialize pyte screen with the requested size - self._screen = pyte.Screen(width, height) - self._stream = pyte.Stream(self._screen) + # Initialize pyte screen with the requested size (under lock to prevent races) + async with self._screen_lock: + self._screen = pyte.Screen(width, height) + self._stream = pyte.Stream(self._screen) pid, master_fd = pty.fork() self.pid = pid @@ -99,9 +100,10 @@ class TerminalSession(Session): fcntl.ioctl(self.master_fd, termios.TIOCSWINSZ, buf) async def set_terminal_size(self, width: int, height: int) -> None: + # First resize the PTY (blocking call in executor) loop = asyncio.get_running_loop() await loop.run_in_executor(None, self._set_terminal_size, width, height) - # Resize pyte screen to match + # Then resize pyte screen to match (after PTY resize completes) async with self._screen_lock: self._screen.resize(height, width)