diff --git a/frontend/src/simulation/RaspberryPi3Bridge.ts b/frontend/src/simulation/RaspberryPi3Bridge.ts index dc1e9760..4428f2b1 100644 --- a/frontend/src/simulation/RaspberryPi3Bridge.ts +++ b/frontend/src/simulation/RaspberryPi3Bridge.ts @@ -105,7 +105,13 @@ export class RaspberryPi3Bridge { } connect(): void { - if (this.socket && this.socket.readyState !== WebSocket.CLOSED) return; + const state = this.socket?.readyState; + if (state === WebSocket.OPEN || state === WebSocket.CONNECTING) return; + // A socket in CLOSING is on its way out, not usable: the old guard + // treated it as live and returned, so "restart in Linux mode" right + // after a run silently did nothing — no boot, and the toolbar still + // showing Stop. Drop it and open a fresh one. + this.socket = null; const base = API_BASE(); const wsProtocol = base.startsWith('https') ? 'wss:' : 'ws:'; diff --git a/frontend/src/store/useSimulatorStore.ts b/frontend/src/store/useSimulatorStore.ts index a2cab443..0bf8cea4 100644 --- a/frontend/src/store/useSimulatorStore.ts +++ b/frontend/src/store/useSimulatorStore.ts @@ -1929,11 +1929,18 @@ export const useSimulatorStore = create((set, get) => { // booting a Linux guest (a backend process + ~90 s). The detector // lives in the overlay; `enginePinned` lets the user override it. const decision = decideEngine(boardId, board.enginePinned); + // `running` is set here (not only by the toolbar): the Linux-mode + // button restarts the board directly, and without this the flag + // kept whatever the previous run left, so the UI showed Stop for + // a board that was not running. set((s) => ({ boards: s.boards.map((b) => - b.id === boardId ? { ...b, engineMode: decision.engine } : b, + b.id === boardId + ? { ...b, engineMode: decision.engine, running: true } + : b, ), serialMonitorOpen: true, + ...(s.activeBoardId === boardId ? { running: true } : {}), })); if (decision.engine === 'instant') { const instant = getInstantEngine();