diff --git a/backend/app/services/qemu_manager.py b/backend/app/services/qemu_manager.py index d0dddfae..9b9f7e00 100644 --- a/backend/app/services/qemu_manager.py +++ b/backend/app/services/qemu_manager.py @@ -118,12 +118,18 @@ class QemuManager: async def send_serial_bytes(self, client_id: str, data: bytes) -> None: inst = self._instances.get(client_id) - if inst and inst._serial_writer: - inst._serial_writer.write(data) - try: - await inst._serial_writer.drain() - except Exception as e: - logger.warning('send_serial_bytes drain: %s', e) + if not inst: + logger.warning('send_serial_bytes: no instance for client_id=%s', client_id) + return + if not inst._serial_writer: + logger.warning('send_serial_bytes: %s has no serial writer (qemu not connected yet?)', client_id) + return + logger.info('send_serial_bytes: %s sending %d bytes: %r', client_id, len(data), bytes(data[:32])) + inst._serial_writer.write(data) + try: + await inst._serial_writer.drain() + except Exception as e: + logger.warning('send_serial_bytes drain: %s', e) # ── Boot sequence ───────────────────────────────────────────────────────── diff --git a/frontend/src/components/raspberry-pi/PiTerminal.tsx b/frontend/src/components/raspberry-pi/PiTerminal.tsx index eb7968c0..08f8e400 100644 --- a/frontend/src/components/raspberry-pi/PiTerminal.tsx +++ b/frontend/src/components/raspberry-pi/PiTerminal.tsx @@ -48,6 +48,11 @@ export const PiTerminal: React.FC = ({ boardId }) => { } catch (_) { /* ignore if dimensions not ready */ } + // Without an explicit focus call xterm.js stays passive — onData + // only fires when the DOM element has focus, so users staring at + // a working prompt see no echo because their keystrokes go to + // whatever element had focus at mount time (canvas, code editor). + try { term.focus(); } catch (_) { /* container may not be visible */ } }); termRef.current = term;